Here's my best attempt at an exhaustive list of the keywords added to Python
since version 0.9x (i.e., since the first version I used):
exec changed from a built-in function to a statement
access & since removed
lambda & Guido probably regrets this <0.4 wink>
So everyone jumping on Guido should consider how incredibly rare it is for
Python to grow a new keyword! But Guido too should consider how unusually
conservative he's been in this regard; every decade or so, languages with
hundreds of thousands of users routinely sprout a small batch of new keywords,
and after a couple months even the justified whining dies out.
That said, I personally would add "for element at i in sequence", despite that
two-letter keywords are especially dangerous (users won't be using the *word*
"at"; it's that any letter pair is likely to show up as an abbreviation -- for
automated_teller, advanced_technology, attentuation_rate, aardvarks_tallied
<ahem> ...). The improvements in clarity, convenience and likely efficiency
outweigh the costs on my scale -- but then I don't have to endure the whining,
either! If our scales differ, that's fine.
More importantly, it would be very helpful to develop a consensus on what
kinds of language changes *are* still thought to be possible at this "late
stage" <0.5 snort -- I was implementing massive changes to the ancient &
"standard" Fortran and C for twice as many years as Python's been alive ...>.
That's best driven by finding out what people think is both *so* important and
that Python is *so* bad at (or weak in) that it will significantly damage the
language's chances of greater success. And, no, adding an "at" clause ain't
it <0.1 wink>.
E.g., here's one thing on my short list: in my Python experience, the only
vital area where every attempt at crafting a solution with modules & functions
has proved completely unsatisfactory is the general area of debugging &
instrumentation decorations (whether of the assertion(*), pre/post-condition,
invariant, trace output, or tally-how-often-it-happens varieties). Quality
production code both needs to keep that kind of stuff in, yet not pay runtime
costs for it during the times the code is stable. The "if __name__ ==
'__main__'" idiom for writing module tests is the only trick of this nature
I've seen (or thought of) that passes muster. And I do think that lack will
damage Python's chances in the larger world.
So where does Python really suck eggs bigtime in your view? I already have a
filter set up to toss any msg containing "indentation".
hmm-guess-i'll-never-know-whether-this-msg-made-it-ly y'rs - tim
Tim Peters tim...@msn.com, t...@dragonsys.com
not speaking for Dragon Systems Inc.
(*) BTW, Guido and Ping, wrt "assert" you're both right: it *does* catch >90%
of bugs in C and C++ code, because 90% of bugs in those languages eventually
manifest as unexpected null pointers, uninitialized variables, or array-bound
violations, and those kinds of things are mostly easy to catch with simple
assertions; but in a pleasant language like Python, simple assertions are that
much less helpful, because the system catches that kind of crap for you -- and
truly powerful class consistency checks often require more than a pure
one-liner expression. OTOH, even ABC had a "CHECK" stmt <wink> ...
Nope, don't mind indntation. I have a hard time justifying the scope/shadow
rules though, when I try to evangelisize about python. I would much prefer a
var keyword to define the scope of ALL variables. Then shadowing and scope
would become much clearer.
This could even be made backwardly compatible (I think) with current python. A
'var'ed variable is automatically made global in any enclosed scopes.
Johan
> So everyone jumping on Guido should consider how incredibly rare it is for
> Python to grow a new keyword! But Guido too should consider how unusually
> conservative he's been in this regard; every decade or so, languages with
> hundreds of thousands of users routinely sprout a small batch of new keywords,
> and after a couple months even the justified whining dies out.
>
> That said, I personally would add "for element at i in sequence", despite that
> two-letter keywords are especially dangerous (users won't be using the *word*
> "at"; it's that any letter pair is likely to show up as an abbreviation -- for
> automated_teller, advanced_technology, attentuation_rate, aardvarks_tallied
> <ahem> ...). The improvements in clarity, convenience and likely efficiency
> outweigh the costs on my scale -- but then I don't have to endure the whining,
> either! If our scales differ, that's fine.
I'd think "for (index element) in sequence" would be cleaner, more
in keeping with Python, and it doesn't require a new keyword. It would break
all that code that loops over sequences of tuples though.
Mike McDonald
mik...@engr.sgi.com
You express this with too much gentleness to C and
its allies. Beside all that you write here, C-cod-
ings also *do* much less than superficially
comparable Python applications, precisely because
memory management and such occupy some large per-
centage of a C programmer's consciousness. So
"assert" is going to be at least two orders of
magnitude less useful in Python.
--
Cameron Laird http://starbase.neosoft.com/~claird/home.html
cla...@NeoSoft.com +1 713 623 8000 #227
+1 713 996 8546 FAX
So what's your point???
--Guido van Rossum (home page: http://www.python.org/~guido/)
>To: Mike McDonald <mikemac@cthulhu>
>Subject: Re: Why Python stinks
>From: Guido van Rossum <gu...@CNRI.Reston.VA.US>
>Date: Fri, 07 Feb 1997 17:35:00 -0500
Nothing in particular. Just IF you were going to add multiple
variable loops for sequences, I'd prefer this syntax. And I wouldn't
be bothered by the backwards compatibility issue since I don't have
any sequences of tuples. That's all.
Mike McDonald
mik...@engr.sgi.com
You don't seem to get it. The whole point of the exercise is to find
some syntax that doesn't break existing code. Looping over sequences
of tuples happens all over the place.
>To: Mike McDonald <mikemac>
>Subject: Re: Why Python stinks
>From: Guido van Rossum <gu...@CNRI.Reston.VA.US>
>Date: Fri, 07 Feb 1997 22:48:18 -0500
Oh, I do get it. Adding new keywords is just as bad as changing
existing symantics. If you want to add keywords, pick really long
names. They're much less likely to be in use than shorter ones. Random
strings work even better! Or use the old Fortran naming conventions!
Probably not many Python programmers use that!
Mike McDonald
mik...@engr.sgi.com
Tim wants tools to help write production code, dealing with assertions,
pre/post-conditions, etc. I'd certainly like some of those, more
because I like the notion of design by contract than because I write
production code (I don't, mostly).
Another oft-mentioned need is the ability to characterize variables to
allow optimizations and the like, such as Don's proposal a few months
back. I certainly feel that while in many cases speed is *not* the
issue, it would help Python's marketing efforts if the interpretive
nature of the language could be turned off selectively in certain cases.
This is related to Ping's proposal to develop a 'static' python. I'd
suggest that those who are currently looking at various ways of
speeding up the current Python keep an eye out for places where things
like variable attributes could allow considerable speedups.
The two domains have some overlap, of course (constant expressions
afford both optimizations and typechecking, for example).
--david
I do not think Python 'sucks eggs' at all, and I've used enough things
which _do_, to know.
However, it will be nice to index things with more than just slices.
Later,
Andrew Mullhaupt
>>>>> "DA" == David Ascher <d...@maigret.cog.brown.edu> writes:
DA> Tim wants tools to help write production code, dealing with
DA> assertions, pre/post-conditions, etc. I'd certainly like some
DA> of those, more because I like the notion of design by contract
DA> than because I write production code (I don't, mostly).
That's the one area where I'd personally like to see more support for:
the ability to define interfaces separate from the implementation.
I think one of the best languages I've used so far in this regard is
Objective-C, with it's clean separation between interface and
implementation, and the parallel notion of protocols and the
definition of a class's interface though the combination of its
inherited interface and protocol conformance.
Python's got all the dynamicity that I learned to love with Obj-C, and
if it ever got static typing (which I personally don't think it needs)
I hope the approach would be something like Obj-C's.
-Barry
Agreed.. I do it all the time, and I have many methods/functions that
loop over either a List or a Tuple without really knowing what it is.
So, what is the state of this "for blah blah in bloo:" jazz?
.jPS
So, any message quoting you gets tossed? :)
Personally, I love the indentation. On friday I inherited a small batch
of Java code to look over and rewrite. The person who wrote the Applet
is new to Java and did a lot of the programming in Windows Notepad,
meaning grouping was a pain to figure out. Fortunately, my text editor
can indent/dedent chunks of code (very handy in Python programming) and
soon I had the code in a way that I could read it, which ended up
looking rather Python-esque. If he had been programming in Python, that
'cleanup' step would never have been needed. If the code had been
bigger, I shudder to think at the mess it could have been.
Now, for my rant:
I've been looking at the language "Dylan" lately, which is a dynamic
language that's kindof a mix of Java, Object Pascal, Smalltalk, and
Lisp. (Marlais, a Dylan-esque language allows code in both Prefix and
Infix notation). One of the main things I like about Smalltalk and
Dylan is their use of Object classes/instances, which are all part of
the same tree. I think (from what little reading I've done) that it
allows type checking based on inheritance. i.e., if you want to check
and make sure something is a Number and if Number is a metaclass that
integer, float, complex, and any user-created classes are derived from,
you can just check against number. It would be nice if there was some
sort of actual Object tree in Python that everything was a part of, and
any user-created class with no set parent is automatically subclassed
off of Object. So, you could have the following code:
> class MyMeta:
> foo=55
>
> class MyCheese(MyMeta):
> spam='bloody vikings'
>
> nurse=MyCheese()
> if type(nurse) == type(MyMeta()):
> print "Celebration!"
A bit cleaner than trying to catch any subclass through Raise..Except
statements. Some benefits would be that if a new class were made off of
UserList, and UserList was actually a subtype of List or PyList or
something like that, the instance of the new class could be compared to
just List. Useful when it doesn't matter whether a Parameter is a
normal list or a user-created mimicry, and easier to code that trying to
read an index and catching an error that would let you know it's not a
list you're dealing with.
Just my $.02, with a couple of quarters to boot :)
.jPS
>>>>> "JPS" == Jeffrey P Shell <je...@cynapses.com> writes:
JPS> One of the main things I like about Smalltalk and
JPS> Dylan is their use of Object classes/instances, which are all
JPS> part of the same tree. I think (from what little reading
JPS> I've done) that it allows type checking based on inheritance.
JPS> i.e., if you want to check and make sure something is a
JPS> Number and if Number is a metaclass that integer, float,
JPS> complex, and any user-created classes are derived from, you
JPS> can just check against number.
JPS> Some benefits would be that if a new class were made off of
JPS> UserList, and UserList was actually a subtype of List or
JPS> PyList or something like that, the instance of the new class
JPS> could be compared to just List.
Not to sound like a broken record, but the way I'd like to see this
accomplished in Python is through support for some kind of "protocols"
or "interfaces" mechanism, a la Objective-C. Checking for inheritance
is IMHO a misguided approach. In a dynamic language like Python,
inheritance should be primarily used as an implementation convenience.
It's the interface of objects (and by that I mean their method
signatures) that determine what you can do with them. Protocols would
be a way to group common methods together, and to test for conformance
of objects.
Here's some pseudocode for Python 3.12 (i.e. fictional :-) of how I
envision this might look:
-------------------- snip snip --------------------
protocol SeqMapProtocol:
def __len__(self)
def __getitem__(self, key)
def __setitem__(self, key)
def __delitem__(self, key)
protocol MappingProtocol(SeqMapProtocol):
def items(self)
def keys(self)
def values(self)
def has_key(self)
def UserDict<MappingProtocol>:
# ...
d = UserDict()
l = []
m = {}
def run_through_keys(dict):
if not dict.conformsTo(MappingProtocol):
raise TypeError, 'not a Mapping:' + `dict`
for k in dict.keys():
do_something_with(k)
-------------------- snip snip --------------------
You could almost already do this with Python 1.4. Change "protocol"
to "class" and put the following after every def line:
: raise RuntimeError, 'unimplemented in most derived class'
Next, instead of conformsTo() being a method of every object (not just
instance), define conformsTo as a function using the try/except
trick. Built-in types are the trick.
Next use standard inheritence notation UserDict(MappingProtocol)
instead of my example using angle brackets.
The trick of course, is adding protocol conformance to the built-in
types and integrating everything so that it works from both the C
layer and the Python layer.
I'd also want to see a convenient way of defining method
implementations outside a class definition, e.g.:
-------------------- snip snip --------------------
class MyClass:
def __init__(self)
def merge_with(self, other)
def dump(self)
def MyClass.__init__(self):
self.__a = 7
def MyClass.merge_with(self, other):
# ...
def MyClass.dump(self):
print 'I am worth', self.__a
-------------------- snip snip --------------------
The two differences here are that including a method definition with
no colon/body suite would be a short-cut for raising a standard
exception (I'd actually define a new built-in exception
UnimplementedError). Also, the notation of
def Class.method(): ...
would be a short hand for something like:
def method(self, ...): ...
Class.method = method
except that things like private names would have to be taken care of,
and read-only attributes like __init__() could be legally defined
outside the class definition.
I hope we can explore the issues of programming-by-contract or
separating interface from implementation in Python. If we can come up
with a natural syntax and clear semantics, I might try to hack
something up to give folks a chance to play with these ideas. This is
the one thing I really think Python needs, and if it had it, it would
be a much better language for writing large systems. Besides, don't
you just hate it when a Python class definition spans 4 or 5 pages of
70 lines or more? :-)
-Barry
Me too!
But as usual, I fear that the main discussion will be about whether
conforms_to
or conformsTo or conformsto or isConforming or implements is the correct
name ;-)
Cheers /F
Sorry to change the Subject line yet again...
>>>>> "BAW" == Barry A Warsaw <bwa...@cnri.reston.va.us> writes:
BAW> Not to sound like a broken record, but the way I'd like to
BAW> see this accomplished in Python is through support for some
BAW> kind of "protocols" or "interfaces" mechanism, a la
BAW> Objective-C.
I am mixing up two separate concepts. I've thought about it a bit
more and I discover I really want two different things:
1. Interfaces. I want to be able to separate the implementation of a
class from its definition. Currently, Python requires you to
provide the implementation for a method right in line with the
class's definition. Yes, I know there are ways within the existing
language to (mostly) accomplish this. I'm interested in a more
natural Pythonesque way of encouraging (dare I say enforce? yikes!
:-) this programming style.
2. Protocols. Grouping collections of method signatures together into
a common behavior such that classes can implement this behavior,
and instances can be tested for conformance to these behavior
collections. Yes, I know there are ways within the existing
language to (mostly) accomplish this. I'm interested in a more
natural Pythonesque way of encouraging (dare I say enforce? yikes!
:-) this programming style.
Is the distinction clear? The are related concepts, but I think the
demands on the language are different, and they really can be used
independently of each other.
-Barry
>>>>> "BAW" == Barry A Warsaw <bwa...@cnri.reston.va.us> writes:
BAW> 1. Interfaces. I want to be able to separate the
BAW> implementation of a class from its definition.
Since I've thought more concretely about Interfaces than Protocols,
I'll start with what I'd like to see from the language to support this
idiom. I think the changes to the core language might be minimal.
Let me start by showing a typical .py file using this new style.
Everyone's familiar with UserDict.py, right?
-------------------- snip snip --------------------UserDict.py
# A more or less complete user-defined wrapper around dictionary objects
from interfaces import unimplemented
# exported class definition
class UserDict:
def keys(self): unimplemented()
def items(self): unimplemented()
def values(self): unimplemented()
def has_key(self, key): unimplemented()
# implementation
def UserDict.__init__(self):
# __data gets correctly mangled!
self.__data = {}
def UserDict.__repr__(self):
# note this method need not be defined in the exported class
# definition. Clients don't need to call this method directly
return repr(self.__data)
def UserDict.__cmp__(self, dict):
if type(dict) == type(self.__data):
return cmp(self.__data, dict)
else:
return cmp(self.__data, dict.__data)
def UserDict.__len__(self): return len(self.__data)
def UserDict.__getitem__(self, key): return self.__data[key]
def UserDict.__setitem__(self, key, item): self.__data[key] = item
def UserDict.__delitem(self, key): del self.__data[key]
def UserDict.keys(self): return self.__data.keys()
def UserDict.items(self): return self.__data.items()
def UserDict.values(self): return self.__data.values()
def UserDict.has_key(self, key): return self.__data.has_key(key)
-------------------- snip snip --------------------
interfaces.unimplemented() might just raise a new standard exception
called UnimplementedError. It might take an optional argument which
is the message to raise as the value of the exception.
The only change to the language would be that dots (yes, possibly more
than one) would be allowed in the funcname part of the def statement.
This can be emulated with existing language features except for two
key points: 1) read-only attributes like __init__() can't currently be
set; 2). Name mangling of private symbols would have to be preserved
in the out-of-class defs. I previously posted a way to do this with
existing language features, modulo those two points.
In the above example, the implementation follows the definition, but
there's no reason why they couldn't be separated into different files.
Python wouldn't do anything magical in the implementation file;
i.e. if `UserDict' wasn't in the current namespace, or it didn't have
a __dict__ attribute, exceptions would be raised. I think the typical
usage would be something like this:
-------------------- snip snip --------------------UserDict.py
# A more or less complete user-defined wrapper around dictionary objects
from interfaces import unimplemented
# exported class definition
class UserDict:
def keys(self): unimplemented()
def items(self): unimplemented()
def values(self): unimplemented()
def has_key(self): unimplemented()
import UserDictImpl
-------------------- snip snip --------------------UserDictImpl.py
# most clients of UserDict never need to look at this file!
from UserDict import UserDict
# implementation
def UserDict.__init__(self):
# __data gets correctly mangled!
self.__data = {}
def UserDict.__repr__(self):
# note this method need not be defined in the exported class
# definition. Clients don't need to call this method directly
return repr(self.__data)
def UserDict.__cmp__(self, dict):
if type(dict) == type(self.__data):
return cmp(self.__data, dict)
else:
return cmp(self.__data, dict.__data)
def UserDict.__len__(self): return len(self.__data)
def UserDict.__getitem__(self, key): return self.__data[key]
def UserDict.__setitem__(self, key, item): self.__data[key] = item
def UserDict.__delitem(self, key): del self.__data[key]
def UserDict.keys(self): return self.__data.keys()
def UserDict.items(self): return self.__data.items()
def UserDict.values(self): return self.__data.values()
def UserDict.has_key(self, key): return self.__data.has_key(key)
-------------------- snip snip --------------------
I just had a kitchen chat with Fred Drake and Roger Masse, and they
came up with a couple of neat takes on this idea. Fred suggested that
one might use this for autoloading. For example, say module
interfaces had another function in it called autoload(), so that when
you might define UserDict.py like so:
-------------------- snip snip --------------------
# A more or less complete user-defined wrapper around dictionary objects
from interfaces import autoload
# exported class definition
class UserDict:
def keys(self): autoload()
def items(self): autoload()
def values(self): autoload()
def has_key(self): autoload()
-------------------- snip snip --------------------
calling any of these methods would cause UserDictImpl.py to be
automatically loaded, providing the implementations to be defined.
I'm not sure how autoload() would be defined; perhaps you'd have to
pass in the module name that the implementations would be defined in.
Roger came up with a different twist. What if the method
implementations were in a C extension? It would be really cool if you
could write the class definitions in Python, but the method
implementations in C, i.e. if you had a UserDictImpl.so with the
method implementations!
Finally, we talked about what an automatic documentation tool might do
with this idiom. What if there were a way for an autodoco tool to
just import the class definitions, getting all the exported method
interfaces and the docstrings, but shortcircuiting the importing of
the implementations? You could do this if the autodoco tool overrode
__import__ and put a special attribute in __builtin__ (or whereever).
Then you might have at the top of the UserDictImpl.py file something
like:
import autodoco
if autodoco.working:
raise autodoco.shortcircuit
which the autodoco's __import__ would catch and ignore.
So whadja think? Would anybody else find this useful and/or cool?
What are the problems with changing the def statement? Would any
other language changes be necessary (I don't think so). Is it *good*
for Python?
Can you guess my answer to the latter is "yes, very"? :-)
-Barry
It's obvious. :) getattr, setattr, hasattr all contain no capitals or
underscores. That leaves "implements" or "conformsto".
Ping
Hmm. Well, i think that changing the def statement to allow
the resulting function to get assigned to any lvalue would
be a nice generalization, but...
I'm not sure you actually need that. In your example couldn't
you just define the implementation in class UserDictImpl in
UserDictImpl.py, and then replace the interface wrapper with
"UserDict.UserDict = UserDictImpl"?
Ping
This would require writing the "class ...:" at the top; while
o.k. from the perspective of simplicity, it means that someone would
actually have to import the implementation (possibly the interface
definition module after the "protocol" classes). I think this is not
the right way to go.
Talking with Barry the other day, we figured that *not* importing
the implementation could be useful, allowing the protocol class to be
imported (say, for docstring extraction) without having to load large
amounts of supporting code. Then we could do the following:
import protocol
class MyProtocol:
def action(self, stuff):
"""Do something...."""
protocol.autoload()
def lounge(self):
"""Don't do anything...."""
protocol.unimplemented()
Any sort of type constraint specifications could be done here as
well if a constraint notation is adopted, or do it the hard way:
class MyProtocol:
def method(self, integer, real):
"""Document this...."""
int(integer)
float(real)
m = protocol.implementation()
r = m(self, integer, real)
protocol.conform(r, protocol.SEQUENCE)
return r
The catch for all this is having a day or two to write & test the
protocol support module. ;-(
-Fred
--
Fred L. Drake, Jr.
fdr...@cnri.reston.va.us
Corporation for National Research Initiatives
1895 Preston White Drive
Reston, VA 20191-5434
Any decent autodoco support would have to be written so that this is
not needed; extra cruft is evil. Replacing the __import__ should be
sufficient. It would also have to be ni-aware.
While I see how you separate "interface" and "protocol", I think another
separation is, perhaps, more useful.
I'd suggest a separation between "specifications" and "implementations".
Of course, a specification is much like an interface, but I would
further divorse it from a class. In your scheme, you model class
interfaces. That is, your "interface" is a view of a specific class.
To me, a class is inherently an implementation artifact. A classic
confusion in OO languages is between class hierarchies and "is-a"
(i.e. specification) hierarchies.
I think there should be a separate notion of a specification that is
totally independent from class. I'm not wed to the term "specification"
and I'm not opposed to the use of class objects to model specification
meta-data for run-time use, although, to truely model specifications, it
might be better to use a specification language, like Z.
In this scheme, a protocol is simply a focused specification. An
object may suport multiple protocols/specifications. And specifications
should be composable.
At the language level, it would be useful if there were a way to very
quickly check whether an object supported a specifications. I think the
programmer should be able to assert that a particular class or object
obeys one or more specifications. The language should not try to check
these assertions. Such an assertion made on a class should apply to
it's instances, but perhaps not to it's sublasses. Elsewhere, there
should be a way to determine what assertions have been made on behaf of
an object:
if supports(myobject,some_specifications): ...
for behavior in behaviorsof(myobject): ...
I have some done some thought experiments about some hacks to add some
run-time minimal checking of interfaces that could be turned off easily.
For example, if we *did* model interfaces as classes, one might define
interface "methods" in which argument defaults were really interfaces
and method bodies were preconditions:
class BoundedStackInterface:
def push(self, object=None):
'add an element to the stack'
if self.isFull(): raise 'Full', self
def pop(self, size=Integer):
'remove some number of elements'
if self.isEmpty(): raise 'Empty', self
def isFull(self): pass
def isEmpty(self): pass
class foo(WithInterfaces):
Interfaces=(BoundedStackInterface,)
max_size=10
def __init__(self): self.data=[]
def push(self,o): self.data.append(o)
def pop(self,size):
r=self.data[-size:]
del self.data[-size:]
return r
def isFull(self): return len(self.data) >= self.max_size
def isEmpty(self): return not self.data
With a suitably constructed WithInterface base class and the ability
to define custom "method" types, one could arrange that when a method
of a foo instance was called, the arguments and precondition in the
corresponding interface method could be checked against the the actual
arguments (including self). This check could be selectively enabled
during development and disabled later without changing application code.
The machinery needed for this is available in my ExtensionClass
mechanism, which has specific hooks for user-defined method types.
Of course, this is just a thought experiment, not a proposal. :-)
Jim