Referring to class methods in class attributes

Steve Holden steve at holdenweb.com
Thu Feb 18 14:30:35 CET 2010


mk wrote:
> Bruno Desthuilliers wrote:
>>> Thanks, that worked. But in order to make it work I had to get rid of
>>> 'self' in print_internal_date signature
>>
>> Indeed. Using it that way, the print_internal_date will not be wrapped
>> in a method object.
> 
> Hold on! How does Python know what to wrap and what not to wrap,
> assuming of course programmer doesn't use @classmethod or @staticmethod?
> Bc self has no special significance, it's just a (strong) convention,
> Python can't know what's the first argument of a function supposed to
> be, self or regular argument, and therefore it has no way of
> differentiating between functions (defined in class body) designed to
> become methods and those that are not?
> 
> Where can I read on Python internals like this (aside from post of
> yours, that is)? Bc  frankly skimming http://docs.python.org/reference/
> didn't give me impression that a lot on the subject is there (well
> there's some, I found smth akin to your explanation below, although
> yours is way more readable)?
> 
> Thanks for explanation below -- I'm going to ask some related questions.
> 
>> Mmmm... Let's try to explain the whole damn thing. It's really (and IMHO
>> beautifully) simple once you get it, but I agree it's a bit peculiar
>> when compared to most mainstream OO languages.
>>
>> The first thing is that the def statement *always* yield a function
>> object. Always. If you don't believe it, try the following snippet:
>>
>> class Foo(object):
>>     def bar(self):
>>         return "baaz"
>>
>> print Foo.__dict__.keys()
>> print type(Foo.__dict__['bar'])
> 
> Just one thing here:
> 
>>>> Foo.bar
> <unbound method Foo.bar>
> 
> Huh?! Why does it say 'unbound' method? Shouldn't that be bound method
> (bound to Foo, that is)?
> 
No. The "unbound method" means it's a callable class attribute. The
significance of "unbound" is that no specific instance is attached.

>> So, why is it that type(Foo.bar) != type(Foo.__dict__['bar']) ?
> 
>>>> type(Foo.__dict__['bar'])
> <type 'function'>
>>>> type(Foo.bar)
> <type 'instancemethod'>
> 
> instancemethod - now that's something new.
> 
Well "instancemethod" is just the type of the attribute. If you create a
Foo instance, its bar method also has type instancemethod, even though
it's a *bound* method:

>>> foo = Foo()
>>> foo
<__main__.Foo object at 0x7ff2a16c>
>>> foo.bar
<bound method Foo.bar of <__main__.Foo object at 0x7ff2a16c>>
>>> type(foo.bar)
<type 'instancemethod'>
>>>

Note that it's only when the method is looked up *from an instance of
the class* does the interpreter create the bound method. And remember
that this is behavior that's specific to Python 2.
> 
>> The
>> answer is : attribute lookup rules and the descriptor protocol.
>>
>> To make a long story short, the descriptor protocol specify that, when,
>> during an attribute lookup, a name resolves to a class attribute AND
>> this attribute has a __get__ method, then this __get__ method is called
>>  (with either the instance or None and the class itself as arguments)
> 
> Depending, I assume, on whether this is instance call | class method
> call, respectively?
> 
Yes.

> Hmm why does the __get__ receive class as argument on top of instance |
> None? After all, when having an instance, the class can always be found
> by instance.__class__ ? Is this for sake of class methods?
> 
When Bruno wrote "... AND this attribute has a __get__ method ...", the
__get__method has to be defined on the attribute's class - the
interpreter won't even look at the instance when trying to resolve the
reference. But inheritance, of course, means that the same __get__
method may be used by several classes, and when there is no instance the
specific (sub)class in question must be identifiable. So you got that right.

> Python is science, I gather: an answer to one question bears another 10
> questions.
> 
Yes, but it isn't quite "turtles all the way down". Ultimately the
behavior we are discussing is hard-wired into the interpreter at the
__getattribute__ level.

>> and whatever it returns becomes the result of the attribute lookup. This
>> mechanism is what provides support for computed attributes.
>>
>> Now the trick is that the function type do implement the descriptor
>> protocol. So when a function is an attribute of a class object and you
>> try to access it as an attribute of either the class itself or an
>> instance of the class, it's __get__ method is called with the instance
>> (or None) and the class. 
> 
>> Having access to itself (of course),
> 
> Quick question: how does a function access itself? Aside from rejected
> PEP (http://www.python.org/dev/peps/pep-3130/) I don't see the way of
> accessing itself outside globals() (and even then how would a function
> know its name -- well it shouldn't care about it really, as function
> object doesn't care how it's labelled, right?). Or does in "real Python"
> func's __get__ receive its own function (func) as an argument, like in
> your example implementation below?
> 
The function is an object of type function, so the lookup triggers a
call to the __get__() method of the function's class, providing the
instance (that is the function that is being called) as the first argument.

>> the
>> instance (if there's one) and the class, it's easy for it to wrap all
>> this into a method object. Which is itself a callable object, that when
>> called mostly inject the instance as first object in the argument's list
>> and returns the result of calling the wrapped function object.
> 
> Aha! So that's the mechanism that makes self magically appear in an
> argument list! I always wondered how it worked. !!THANKS!!
> 
> 
>> My 2 cents...
> 
> Well, Bruno -- that was more like $200!
> 
I agree, this is stuff that's hard to understand, and Bruno's
explanations are most helpful.

regards
 Steve
-- 
Steve Holden           +1 571 484 6266   +1 800 494 3119
PyCon is coming! Atlanta, Feb 2010  http://us.pycon.org/
Holden Web LLC                 http://www.holdenweb.com/
UPCOMING EVENTS:        http://holdenweb.eventbrite.com/




More information about the Python-list mailing list