I haven't even finished reading this yet. This is good stuff! --- Kevin Jacobs <jacobs@penguin.theopalgroup.com> wrote: > Hello all, > > I've been meta-reflecting a lot lately: reflecting > on reflection. > > My recent post on __slots__ not being picklable (and > the resounding lack of > response to it) inspired me to try my hand at > channeling Guido and reverse- > engineer some of the design decisions that went into > the new-style class > system. Unfortunately, the more I dug into the > code, the more philosophical > my questions became. So, I've written up some > questions that help lay bare > some of basic design questions that I've been asking > myself and that you > should be aware of. > > While there are several subtle issues I could raise, > I do want some feedback > on some simple and fundamental ones first. Please > don't disqualify yourself > from commenting because you haven't read the code or > used the new features > yet. I've written my examples assuming only a basic > and cursor > understanding of the new Python 2.2 features. > > [In this discussion I am only going to talk about > native Python classes, > not C-extension or native Python types (e.g., > ints, lists, tuples, > strings, cStringIO, etc.)] > > 1) Should class instances explicitly/directly know > all of their attributes? > > Before Python 2.2, all object instances > contained a __dict__ attribute > that mapped attribute names to their values. > This made pickling and > some other reflection tasks fairly easy. > > e.g.: > > class Foo: > def __init__(self): > self.a = 1 > self.b = 2 > > class Bar(Foo): > def __init__(self): > Foo.__init__(self) > self.c = 3 > > bar = Bar() > print bar.__dict__ > > {'a': 1, 'c': 3, 'b': 2} > > I am aware that there are situations where this > simple case does not > hold (e.g., when implementing __setattr__ or > __getattr__), but let's > ignore those for now. Rather, I will > concentrate on how this classical > Python idiom interacts with the new slots > mechanism. Here is the above > example using slots: > > e.g.: > > class Foo(object): > __slots__ = ['a','b'] > def __init__(self): > self.a = 1 > self.b = 2 > > class Bar(Foo): > __slots__ = ['c'] > def __init__(self): > Foo.__init__(self) > self.c = 3 > > bar = Bar() > print bar.__dict__ > > AttributeError: 'Bar' object has no > attribute '__dict__' > > We can see that the class instance 'bar' has no > __dict__ attribute. > This is because the slots mechanism allocates > space for attribute > storage directly inside the object, and thus > does not use (or need) a > per-object instance dictionary to store > attributes. Of course, it is > possible to request that a per-instance > dictionary by inheriting from a > new-style class that does not list any slots. > e.g. continuing from > above: > > class Baz(Bar): > def __init__(self): > Bar.__init__(self) > self.d = 4 > self.e = 5 > > baz = Baz() > print baz.__dict__ > > {'e': 5, 'd': 4} > > We have now created a class that has __dict__, > but it only contains the > attributes not stored in slots! So, should > class instances explicitly > know their attributes? Or more precisely, > should class instances > always have a __dict__ attribute that contains > their attributes? Don't > worry, this does not mean that we cannot also > have slots, though it > does have some other implications. Keep > reading... > > > 2) Should attribute access follow the same > resolution order rules as > methods? > > class Foo(object): > __slots__ = ['a'] > self.a > def __init__(self): > self.a = 1 > > class Bar(Foo): > __slots__ = ('a',) > def __init__(self): > Foo.__init__(self) > self.a = 2 > > bar = Bar() > print bar.a > > 2 > print super(Bar,bar).a # this doesn't > actually work > > 2 or 1? > > Don't worry -- this isn't a proposal and no, > this doesn't actually work. > However, the current implementation only > narrowly escapes this trap: > > print bar.__class__.a.__get__(bar) > > 2 > print bar.__class__.__base__.a.__get__(bar) > > AttributeError: a > > Ok, let me explain what just happened. Slots > are implemented via the > new descriptor interface. In short, descriptor > objects are properties > and support __get__ and __set__ methods. The > slot descriptors are told > the offset within an object instance the > PyObject* lives and proxy > operations for them. So getting and setting > slots involves: > > # print bar.a > a_descr = bar.__class__.a > print a_descr.__set__(bar) > > # bar.a = 1 > a_descr = bar.__class__.a > a_descr.__set__(bar, 1) > > So, above we get an attribute error when trying > to access the 'a' slot > from Bar since it was never initialized. > However, with a little > ugliness you can do the following: > > # Get the descriptors for Foo.a and Bar.a > a_foo_descr = bar.__class__.__base__.a > a_bar_descr = bar.__class__.a > a_foo_descr.__set__(bar,1) > a_bar_descr.__set__(bar,2) > > print bar.a > > 2 > print a_foo_descr.__get__(bar) > > 1 > print a_bar_descr.__get__(bar) > > 2 > > In other words, the namespace for slots is not > really flat, although > there is no simple way to access these hidden > attributes === message truncated === __________________________________________________ Do You Yahoo!? Yahoo! Sports - Coverage of the 2002 Olympic Games http://sports.yahoo.com
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4