Reminder: Python track at OSCON -- Deadline March 1!
Reminder: The O'Reilly Open Source Convention (July 22-26, 2002 -- San Diego, CA) is accepting proposals for tutorials, talks, panels, and lightning talks. See the Call for Participation in the Python and Zope track on python.org. Proposals are due by March 1, so don't wait a moment longer! Details available at: CFP URL: http://www.python.org/workshops/oscon2002/cfp.html form: http://conferences.oreillynet.com/cs/os2002/create/e_sess Cheers, -- David Ascher [Guido's the program chair, but he's on the road, so I'm filling in] PS: Feel free to resend this to whatever mailing lists may be interested.
[I tried to post this on SourceForge, but as usual, it hates my guts] I have been hacking on ways to make lighter-weight Python objects using the __slots__ mechanism that came with Python 2.2 new-style class. Everything has gone swimmingly until I noticed that slots do not get pickled/cPickled at all! Here is a simple test case: import pickle,cPickle class Test(object): __slots__ = ['x'] def __init__(self): self.x = 66666 test = Test() pickle_str = pickle.dumps( test ) cpickle_str = cPickle.dumps( test ) untest = pickle.loads( pickle_str ) untestc = cPickle.loads( cpickle_str ) print untest.x # raises AttributeError print untextc.x # raises AttributeError Clearly, this is incorrect behavior. The problem is due a change in object reflection semantics. Previously (before type-class unification), a standard Python object instance always contained a __dict__ that listed all of its attributes. Now, with __slots__, some classes that do store attributes have no __dict__ or one that only contains what did not fit into slots. Unfortunately, there is no trivial way to know what slots a particular class instance really has. This is because the __slots__ list in classes and instances can be mutable! Changing these lists _does not_ change the object layout at all, so I am unsure why they are not stored as tuples and the '__slots__' attribute is not made read-only. To be pedantic, the C implementation does have an immutable and canonical list(s) of slots, though they are well buried within the C extended type implementation. So, IMHO this bug needs to be fixed in two steps: First, I propose that class and instance __slots__ read-only and the lists made immutable. Otherwise, pickle, cPickle, and any others that want to use reflection will be SOL. There is certainly good precedent in several places for this change (e.g., __bases__, __mro__, etc.) I can submit a fairly trivial patch to do so. This change requires Guido's input, since I am guessing that I am simply not privy to the method, only the madness. Second, after the first issue is resolved, pickle and cPickle must then be modified to iterate over an instance's __slots__ (or possibly its class's) and store any attributes that exist. i.e., some __slots__ can be empty and thus should not be pickled. I can also whip up patches for this, though I'll wait to see how the first issue shakes out. Regards, -Kevin PS: There may be another problem when when one class inherits from another and both have a slot with the same name. e.g.: class Test(object): __slots__ = ['a'] class Test2(Test): __slots__ = ['a'] test=Test() test2=Test2() test2.__class__ = Test This code results in this error: Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: __class__ assignment: 'Test' object layout differs from 'Test2' However, Test2's slot 'a' entirely masks Test's slot 'a'. So, either there should be some complex attribute access scheme to make both slots available OR (in my mind, the preferable solution) slots with the same name can simply be re-used or coalesced. Now that I think about it, this has implications for pickling objects as well. I'll likely leave this patch for Guido -- it tickles some fairly hairy bits of typeobject. Cool stuff, but the rabbit hole just keeps getting deeper and deeper.... -- Kevin Jacobs The OPAL Group - Enterprise Systems Architect Voice: (216) 986-0710 x 19 E-mail: jacobs@theopalgroup.com Fax: (216) 986-0714 WWW: http://www.theopalgroup.com
Hi Kevin. I'm with you slots have great potential. First of all, I think it is a very bad idea that slots could be mutable. Here is something I wrote on the python-list. (Python list is too noisy and too many dumb questions involving people not thinking before tackle a problem. -with the exception of an interesting thread on licensing, and a spanish message.) Anyway, here is some food for thought on slots. Perhaps long-winded and already talked about, but here it is: (If you want to skip the intro and get to the meat and potatos go to the code, and read my remarks. - - - - - - Hello Python developers! I have been reading about Python 2.2's new features and I have to say there's lots of good stuff going on. The feature I dedicate this message to is slots. This ubiquitous new feature has the potential to boost performance of python quite considerable and it is the first time python has ever hinted at, for lack of a better word, "being static". I used this word loosely. What I mean by this is that typically one could set attributes arbitrarily on python objects, but now cometh slots, if __slots__ is defined, one could set only attribute names defined in slots if __slots__ exists. In introductory essay on the new python object system, "Unifying types and classes in 2_2", I found the benevolent dictator's answer for the existence of __slots__ unsatisfactory (or at least the example). As implied from the introductory essay, __slots__ was used as a means to eliminate a double dictionary for the dictionary subclass in the example (please don't be offended -- SO what). That is one application of slots, but there is a more powerful way to use this new feature. First let me discuss some notational issues I have with __slots__. It is possible to use any object that supports iteration as the __slots__ object (huh does my memory serve me correctly?). I feel that this is not a good idea. Consider the following example: class Foo(object): __slots__=['a','b','c'] foo=Foo() Bad, but you could do it. foo.__class__.__slots__.append('d') #Ouch! Suppose someone wrote code to initialize some instance like so: for attr in foo.__class__.__slots__: setattr(foo,attr,0) Granted this is decidedly bad code but the problem this creates is that attribute "d" does not exist in foo or any instance of Foo before or after the "append event". So an attribute error will be raised. Slot attributes are read only thus del foo.__slots__ is not possible. So neither should __slots__.append be legal. So the answer is to enforce __slots__ is tuple, or for a single attribute __slots__ is string, when compiling objects. Another issue I have with __slots__ is that there exists better notation to do the same thing. And part of python is to maintain a clean coding style. Why not introduce new a keyword into the language so "static attribute sets" are created like so: class Foo(object): attribute a attribute b attribute c Perhaps this syntax could be expanded further to include strict typing: class Foo(object): attribute a is str attribute b is float attribute c is int I feel that this notation is much more lucid and could lead to even more interesting notation: class Foo(object): attribute a is str attribute b is float attribute c is property attribute g is property of float I hope that it is understood from this context that "is" compares the type(<attrname>) to trailing type in the declaration. The meaning of the modifier "of" in the last line is a bit ambiguous at this moment. Perhaps it enforces the type passed into fset, and the return value of fget. The programmer doesn't need to know what the underlying __slots__ structure looks like, unless they are doing some heavy duty C. In which case they could probably handle it in all it's grand ugliness. Is the python interpreter __slot__ aware? Instead of the usual variable name lookup into __dict__ (which is not supported by instances who's class implements __slots__), a slot attribute could be directly referenced via pointer indirection. Does the interpreter substitute references for slot attributes? For that matter any namespace could utilize this new approach, even the local namespace of a code segment. Slots could be built implicitly for a local namespace. This could lead to significant performance boost. Or perhaps a JIT of sorts for python. Cause all you need is a bit of type info, and those segments could be accurately translated into native code. ------ --- Kevin Jacobs <jacobs@penguin.theopalgroup.com> wrote:
[I tried to post this on SourceForge, but as usual, it hates my guts]
I have been hacking on ways to make lighter-weight Python objects using the __slots__ mechanism that came with Python 2.2 new-style class. Everything has gone swimmingly until I noticed that slots do not get pickled/cPickled at all!
Here is a simple test case:
import pickle,cPickle class Test(object): __slots__ = ['x'] def __init__(self): self.x = 66666
test = Test()
pickle_str = pickle.dumps( test ) cpickle_str = cPickle.dumps( test )
untest = pickle.loads( pickle_str ) untestc = cPickle.loads( cpickle_str )
print untest.x # raises AttributeError print untextc.x # raises AttributeError
Clearly, this is incorrect behavior. The problem is due a change in object reflection semantics. Previously (before type-class unification), a standard Python object instance always contained a __dict__ that listed all of its attributes. Now, with __slots__, some classes that do store attributes have no __dict__ or one that only contains what did not fit into slots.
Unfortunately, there is no trivial way to know what slots a particular class instance really has. This is because the __slots__ list in classes and instances can be mutable! Changing these lists _does not_ change the object layout at all, so I am unsure why they are not stored as tuples and the '__slots__' attribute is not made read-only. To be pedantic, the C implementation does have an immutable and canonical list(s) of slots, though they are well buried within the C extended type implementation.
So, IMHO this bug needs to be fixed in two steps:
First, I propose that class and instance __slots__ read-only and the lists made immutable. Otherwise, pickle, cPickle, and any others that want to use reflection will be SOL. There is certainly good precedent in several places for this change (e.g., __bases__, __mro__, etc.) I can submit a fairly trivial patch to do so. This change requires Guido's input, since I am guessing that I am simply not privy to the method, only the madness.
Second, after the first issue is resolved, pickle and cPickle must then be modified to iterate over an instance's __slots__ (or possibly its class's) and store any attributes that exist. i.e., some __slots__ can be empty and thus should not be pickled. I can also whip up patches for this, though I'll wait to see how the first issue shakes out.
Regards, -Kevin
PS: There may be another problem when when one class inherits from another and both have a slot with the same name.
e.g.: class Test(object): __slots__ = ['a']
class Test2(Test): __slots__ = ['a']
test=Test() test2=Test2() test2.__class__ = Test
This code results in this error:
Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: __class__ assignment: 'Test' object layout differs from 'Test2'
However, Test2's slot 'a' entirely masks Test's slot 'a'. So, either there should be some complex attribute access scheme to make both slots available OR (in my mind, the preferable solution) slots with the same name can simply be re-used or coalesced. Now that I think about it, this has implications for pickling objects as well. I'll likely leave this patch for Guido -- it tickles some fairly hairy bits of typeobject.
Cool stuff, but the rabbit hole just keeps getting deeper and deeper....
-- Kevin Jacobs The OPAL Group - Enterprise Systems Architect Voice: (216) 986-0710 x 19 E-mail: jacobs@theopalgroup.com Fax: (216) 986-0714 WWW: http://www.theopalgroup.com
_______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev
__________________________________________________ Do You Yahoo!? Got something to say? Say it better with Yahoo! Video Mail http://mail.yahoo.com
On Fri, 15 Feb 2002, john coppola wrote:
Hi Kevin. I'm with you slots have great potential. First of all, I think it is a very bad idea that slots could be mutable.
They don't have to be mutable -- its more a relic of the implementation. e.g.: class A(object): __slots__ = ('a','b') A.__slots__.append('c') Traceback (most recent call last): File "<stdin>", line 1, in ? AttributeError: 'tuple' object has no attribute 'append' Of course, I think that this is something of a mistake, though I'll reserve final justment until after I've heard Guido's reasoning. Its clear that he has bigger plans for both the syntax and semantics (especially if you heard some of his off-hand remarks at IPC10). -Kevin -- Kevin Jacobs The OPAL Group - Enterprise Systems Architect Voice: (216) 986-0710 x 19 E-mail: jacobs@theopalgroup.com Fax: (216) 986-0714 WWW: http://www.theopalgroup.com
On Friday 15 February 2002 12:57 pm, john coppola wrote:
Another issue I have with __slots__ is that there exists better notation to do the same thing. And part of python is to maintain a clean coding style. Why not introduce new a keyword into the language so "static attribute sets" are created like so:
class Foo(object): attribute a attribute b attribute c
Guido suggested several alternative syntaxes during his presentation on developers day at the Python conference. His slides are online from python.org. He used the word "slot" instead of "attribute". Both names have advantanges. He question the use of the term slot and was looking for an appropriate substitute. It has the advantage of being short and easy to type. Using the term "attribute" would be more descriptive. I find it very long to type and somewhat visually distracting.
Perhaps this syntax could be expanded further to include strict typing:
class Foo(object): attribute a is str attribute b is float attribute c is int
I suggested a method for adding optional static typing to slots and submitted a patch[1] last November. My patch eliminated the special __slots__ member and replaced it with a special method to a class called addmember() that could be used to add slot definitions to a class. A docstring, an optional type, and a default value could be specified as parameters in the addmembers() method. If a type, or tuple of types was defined the tp_descr_set call in C would first check the type of the object being assigned to the member name using the isinstance() builtin. If isinstance failed an exception would be triggered. While my approach was patterened after the property() builtin, the Python Labs crowd didn't like the notation and rejected the patch. (I don't think it helped that the feature freeze was about to start and Guido was out of paternity leave.) Here was example of how the addmember() method worked:
class B(object): """class B's docstring """ a = addmember(types=int, default=56, doc="a docstring") b = addmember(types=int, doc="b's docstring") c = addmember(types=(int,float), default=5.0, doc="c docstring") d = addmember(types=(str,float), default="ham", doc="d docstring") b = B() b.a 56 b.d 'ham'
[1]http://sourceforge.net/tracker/index.php?func=detail&aid=480562&group_id=5470&atid=305470 I think most everyone would agree that adding optional static typing could enable some interesting optimizations, but not everyone at the conference was supportive of the encrouchment of static typing on their favorite dynamiically typed language. Greg Ward presented a proposed syntax for optional static typing at the end of the optimization sesssion on developer's day. Greg had made a presentation on Grouch, his postprocessing type checker for ZODB, during the lightning talks. The proposed syntax for optional static typing was a meld of Guido's proposed new syntax for slots, my addmembers() work, and Greg's Grouch. Guido wasn't keen on the docstring notation, but otherwise seemed to be receptive of the syntax and the idea of adding optional static typing. Here is what Greg presented: # ====================================================================== # WHAT IF... # # Grouch's type syntax were integrated into Python? class Thing: slot name : string "The name of the thing" class Animal (Thing): slot num_legs : int = 4 "Number of legs this animal has" slot furry : boolean = true "Does this animal have full-body fur?" class Vegetable (Thing): # hypothetical constraint (is this type info?) slot colour : string oneof ("red", "green", "blue") "Colour of this vegetable's external surface" class Mineral (Thing): slot atoms : [(atom:Atom, n:int)] """Characterize the proportion of elements in this mineral's crystal lattice: each tuple records the relative count of that atom in the lattice.""" # possible constraints on this attribute: # n > 0 # atom in AtomSet (collection of all atoms) # how to code these? do we even *want* syntax # for arbitrary constraints? or just require that # you code a set_atoms() method? (or a property # modifier, or something)
class B(object): """Docstring of class B """ slot a: int = 56
"""a docstring """ precondition: if b > 3: default=3 slot b: int """Docstring of b """ slot c: int | float = 5.0 """Docstring of attribute c """ slot d: str | float = "spam" """Docstring of d """ postcondition: if self.b = 42.0
b = B() b.a
Michael McLay writes:
While my approach was patterened after the property() builtin, the Python Labs crowd didn't like the notation and rejected the
I'll note as well that at least some of us, if not all, don't like the property() syntax as well. My current favorite was one of Guido's proposals at Python 10: class Foo(object): property myprop: """A computed property on Foo objects.""" def __get__(self): return ... def __set__(self): ... def __delete__(self): ... -Fred -- Fred L. Drake, Jr. <fdrake at acm.org> PythonLabs at Zope Corporation
On Friday 15 February 2002 02:35 pm, Fred L. Drake, Jr. wrote:
Michael McLay writes:
While my approach was patterened after the property() builtin, the Python Labs crowd didn't like the notation and rejected the
I'll note as well that at least some of us, if not all, don't like the property() syntax as well. My current favorite was one of Guido's proposals at Python 10:
I agree with you on this being a better notation. It unclutters the class definition. Had Guido suggested the alternative slot syntaxes back at the start of November I would have used one of the alternative syntaxes instead of creating a new builtin function. BTW, adding a builtin function is a pain. The trick of counting the number of parameters to determine behavior caused strange things to happen during the testing of the addmember function.
class Foo(object): property myprop: """A computed property on Foo objects."""
def __get__(self): return ... def __set__(self): ... def __delete__(self):
Is someone working on an implementation of this?
[Michael McLay]
... Had Guido suggested the alternative slot syntaxes back at the start of November I would have used one of the alternative syntaxes instead of creating a new builtin function.
Guido said several times during 2.2 development that he didn't like using builtin functions for some of the new class features, but that syntax issues were off the table before 2.3 because there wasn't time to address them for 2.2. He didn't repeat this every time it was brought up, though; you can't imagine how pressed for time we all were, and Guido especially.
class Foo(object): property myprop: """A computed property on Foo objects."""
def __get__(self): return ... def __set__(self): ... def __delete__(self):
Is someone working on an implementation of this?
Not within PythonLabs at present.
This is beautiful! It's like an innerclass. Perfect! Encapulated delegate. I can't wait for Python 3.0.
class Foo(object): property myprop: """A computed property on Foo objects."""
def __get__(self): return ... def __set__(self): ... def __delete__(self): ... -Fred
-- Fred L. Drake, Jr. <fdrake at acm.org> PythonLabs at Zope Corporation
__________________________________________________ Do You Yahoo!? Got something to say? Say it better with Yahoo! Video Mail http://mail.yahoo.com
john coppola writes:
This is beautiful! It's like an innerclass. Perfect! Encapulated delegate. I can't wait for Python 3.0.
I can't wait to work on it! -Fred -- Fred L. Drake, Jr. <fdrake at acm.org> PythonLabs at Zope Corporation
On Fri, 15 Feb 2002, Fred L. Drake, Jr. wrote:
john coppola writes:
This is beautiful! It's like an innerclass. Perfect! Encapulated delegate. I can't wait for Python 3.0.
I can't wait to work on it!
In the mean time, does anyone have any comments on the original bug report(s)? Thanks, -Kevin -- Kevin Jacobs The OPAL Group - Enterprise Systems Architect Voice: (216) 986-0710 x 19 E-mail: jacobs@theopalgroup.com Fax: (216) 986-0714 WWW: http://www.theopalgroup.com
Fred L. Drake, Jr. writes: [describing a suggested property syntax]
class Foo(object): property myprop: """A computed property on Foo objects."""
def __get__(self): return ...
Perhaps it was obvious to everyone else, but it just occured to me that this lends itself to inheriting descriptor types: class ReadOnly(object): def __get__(self): raise NotImplementedError("sub-class must override this!") def __set__(self): raise AttributeError("read-only attribute") def __delete__(self): raise AttributeError("read-only attribute") class Foo(object): property myprop(ReadOnly): def __get__(self): return ... -Fred -- Fred L. Drake, Jr. <fdrake at acm.org> PythonLabs at Zope Corporation
Hey Fred. It might not be a good idea to nest the "property class" like an inner class. It may be plausible that property objects are reusable between classes. As implied by this syntax, it wouldn't be reuseable. Another point, is that they may be very large. Which would be messy. I did i bit of brainstorming. One purpose of the type objects is a means to coerce one object to another. So here is the pattern. Just like str(MyObject) requires __str__, or len(MyObject) requires __len__ or any of the factory functions for that matter, the property factory function would require that your object support both __get__ , __set__ , and __del__. Thats it. So instead of, property(fset,fget,fdel)you would instead have, property(AnyObjectSupportingAboveInterface). How the property factory function differs from the others is that it will only check for the existence of these methods, and will not execute the code within them. It instead sets a flag on the object indicating that it is active. Will be necessary to do checking on every object for every set, or every get. Not too bad though. How time consuming is two if statements? Is this making sense? John Coppola --- "Fred L. Drake, Jr." <fdrake@acm.org> wrote:
Fred L. Drake, Jr. writes: [describing a suggested property syntax]
class Foo(object): property myprop: """A computed property on Foo objects."""
def __get__(self): return ...
Perhaps it was obvious to everyone else, but it just occured to me that this lends itself to inheriting descriptor types:
class ReadOnly(object): def __get__(self): raise NotImplementedError("sub-class must override this!")
def __set__(self): raise AttributeError("read-only attribute")
def __delete__(self): raise AttributeError("read-only attribute")
class Foo(object): property myprop(ReadOnly): def __get__(self): return ...
-Fred
-- Fred L. Drake, Jr. <fdrake at acm.org> PythonLabs at Zope Corporation
_______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev
__________________________________________________ Do You Yahoo!? Yahoo! Sports - Coverage of the 2002 Olympic Games http://sports.yahoo.com
Fred L. Drake, Jr. wrote:
Michael McLay writes:
While my approach was patterened after the property() builtin, the Python Labs crowd didn't like the notation and rejected the
I'll note as well that at least some of us, if not all, don't like the property() syntax as well. My current favorite was one of Guido's proposals at Python 10:
class Foo(object): property myprop: """A computed property on Foo objects."""
def __get__(self): return ... def __set__(self): ... def __delete__(self): ...
What's wrong with: class Foo(object): class myprop_class (object): """A computed property on Foo objects.""" def __get__(subclass, self, klass): return ... def __set__(subclass, self, value): ... def __delete__(subclass, self): ... myprop = myprop_class () It's a grand total of _two_ lines more than your example, and has more extensions possibilities to boot. While we're discussing strange usages of blocks, one feature I've always wanted has been subblocks passed to functions. It could be done using a new argument prefix "@" (for example). If the block is not otherwise taken (if it is in an if statement, for example), it can be followed by ":" and a normal block; this block is then put in the argument as a PyCodeObject (I think). The argument can also be given a value normally. The code object also has a few new methods for our convenience. So to implement your example above: def field (@block): dict = block.exec_save_locals () # Execute the block and return the locals dictionary rather than destroy it. fget = dict.get ("__get__", None) fset = dict.get ("__set__", None) fdel = dict.get ("__delete__", None) fdoc = dict.get ("__doc__", None) return property (fget, fset, fdel, fdoc) Now that we have that, we do your example: class Foo(object): myprop = field (): """A computed property on Foo objects.""" def __get__(self): return ... def __set__(self, value): ... def __delete__(self): ... There are other capabilities, but as I've never had a language that can do this I wouldn't know how many pragmatic possibilities there are. The advantage over Guido's method is that his suggestion solves a single problem and has no use outside of it, while mine, at least on the face of it, could be applied in other ways.
Burton Radons wrote:
What's wrong with:
class Foo(object): class myprop_class (object): """A computed property on Foo objects."""
def __get__(subclass, self, klass): return ... def __set__(subclass, self, value): ... def __delete__(subclass, self): ...
How about this: class Foo(object): class myprop(property): ... -- --- Aahz (@pobox.com) Hugs and backrubs -- I break Rule 6 <*> http://www.rahul.net/aahz/ Androgynous poly kinky vanilla queer het Pythonista We must not let the evil of a few trample the freedoms of the many.
participants (8)
-
aahz@rahul.net -
Burton Radons -
David Ascher -
Fred L. Drake, Jr. -
john coppola -
Kevin Jacobs -
Michael McLay -
Tim Peters