Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Referring to class methods in class attributes

8 views
Skip to first unread message

mk

unread,
Feb 17, 2010, 12:38:14 PM2/17/10
to pytho...@python.org
Hello everyone,

OK so I have this:

def print_internal_date(filename):
f = open(filename + 'c', "rb")
data = f.read(8)
mtime = struct.unpack("<i", data[4:])
return time.asctime(time.gmtime(mtime[0]))

class PYFileInfo(FileInfo):
'python file properties'

tagdata = {'compiled_fname': lambda x: x + 'c',
'size': os.path.getsize,
'internal_date': print_internal_date
}

def __init__(self, fname=None):
FileInfo.__init__(self,fname)

def __setitem__(self, key, value):
FileInfo.__setitem__(self, key, value)
if key == 'name' and value:
print value
self.__get_props(value)

def __get_props(self, value):
py_compile.compile(value)
for tag, fun in PYFileInfo.tagdata.items():
self[tag] = fun(value)
...


It works. But if I'd like to def print_internal_date in PYFileInfo body
like so:

class PYFileInfo(FileInfo):
'python file properties'

def print_internal_date(self, filename):
f = open(filename + 'c', "rb")
data = f.read(8)
mtime = struct.unpack("<i", data[4:])
return time.asctime(time.gmtime(mtime[0]))

tagdata = {'compiled_fname': lambda x: x + 'c',
'size': os.path.getsize,
'internal_date': PYFileInfo.print_internal_date
}

I get:

c:/Python26/pythonw.exe -u "C:/mp3i/finfo2.py"
Traceback (most recent call last):
File "C:/mp3i/finfo2.py", line 53, in <module>
class PYFileInfo(FileInfo):
File "C:/mp3i/finfo2.py", line 64, in PYFileInfo
'internal_date': PYFileInfo.print_internal_date
NameError: name 'PYFileInfo' is not defined


Hmm do I get that class data attributes are defined before class is
defined?!

Why I would like to do it this way:

1. keeping all the stuff specific to PYFileInfo where it belongs, in a
class: the print_internal_method does nothing but read timestamp from a
.pyc file, so it's not a generic function.

2. I guess I could define tagdata in __init__ and be done with it:
however, it's an instance attribute then and not class attribute: a few
bytes are wasted. Seriously, however, suppose tagdata or smth like this
is really large? It would make sense to make it class attribute and not
instance attribute. So I'm trying to work out if there's a way to do it.


Regards,
mk

mk

unread,
Feb 17, 2010, 1:38:10 PM2/17/10
to pytho...@python.org
Stephen Hansen wrote:

> You don't have to (and can't) refer to the class within the body. Class
> statements are sort of... odd. They are code which is directly executed,
> and the results are then passed into a metaclass/type/whatever and a
> class object is created. While within the class body, the class doesn't
> exist yet.
>
> But you don't need it to.
>
> Just do:
>
> 'internal_date': print_internal_date
>
> The 'def' is in the same local scope as 'tagdata' is.

Thanks, that worked. But in order to make it work I had to get rid of
'self' in print_internal_date signature, bc all other functions in
tagdata have only a single argument:

class PYFileInfo(FileInfo):
'python file properties'

def print_internal_date(filename):
...

tagdata = {'compiled_fname': lambda x: x + 'c',
'size': os.path.getsize,
'internal_date': print_internal_date
}

That looks weird: a method with no 'self'. Hmm that is probably
seriously wrong.

This obviously means no other method can call it like
self.print_internal_date(), because self would get passed as first
argument, yes?

I checked that print_internal_date can be called on an instance, so the
same method can be seen as:

- class method -- when used in local scope definition like tagdata, or
- instance method -- when called from an instance or from self?

It should be called ______print_internal_date really. ;-)

I wonder if I'm not trying to make Python things it shouldn't be doing,
but it's the problem at hand that is leading me into this conundrum: all
other functions for tagdata use single arguments. I should probably code
around that anyway..

Regards,
mk

Jean-Michel Pichavant

unread,
Feb 17, 2010, 1:53:59 PM2/17/10
to mk, pytho...@python.org
mk wrote:
> Stephen Hansen wrote:
>
>> You don't have to (and can't) refer to the class within the body.
>> Class statements are sort of... odd. They are code which is directly
>> executed, and the results are then passed into a
>> metaclass/type/whatever and a class object is created. While within
>> the class body, the class doesn't exist yet.
>>
>> But you don't need it to.
>>
>> Just do:
>>
>> 'internal_date': print_internal_date
>>
>> The 'def' is in the same local scope as 'tagdata' is.
>
> Thanks, that worked. But in order to make it work I had to get rid of
> 'self' in print_internal_date signature, bc all other functions in
> tagdata have only a single argument:
>
> class PYFileInfo(FileInfo):
> 'python file properties'
>
> def print_internal_date(filename):
> ...
>
> tagdata = {'compiled_fname': lambda x: x + 'c',
> 'size': os.path.getsize,
> 'internal_date': print_internal_date
> }
>
> That looks weird: a method with no 'self'. Hmm that is probably
> seriously wrong.
> [snip]


Absolutely not. It's called a static method, you have to decorate it
with @staticmethod though.

class A:
@staticmethod
def getInstance():
return A()

a = A.getInstance()

you have also the class method version:

class B:
@classmethod:
def getInstance(cls): # class methods take a class as first parameter
return cls()


JM

Bruno Desthuilliers

unread,
Feb 17, 2010, 2:44:47 PM2/17/10
to
mk a écrit :

> Stephen Hansen wrote:
>
>> You don't have to (and can't) refer to the class within the body.
>> Class statements are sort of... odd. They are code which is directly
>> executed, and the results are then passed into a
>> metaclass/type/whatever and a class object is created. While within
>> the class body, the class doesn't exist yet.
>>
>> But you don't need it to.
>>
>> Just do:
>>
>> 'internal_date': print_internal_date
>>
>> The 'def' is in the same local scope as 'tagdata' is.
>
> 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.

> bc all other functions in
> tagdata have only a single argument:
>
> class PYFileInfo(FileInfo):
> 'python file properties'
>
> def print_internal_date(filename):
> ...
>
> tagdata = {'compiled_fname': lambda x: x + 'c',
> 'size': os.path.getsize,
> 'internal_date': print_internal_date
> }
>
> That looks weird: a method with no 'self'.

It's not a method.

> Hmm that is probably
> seriously wrong.
>
> This obviously means no other method can call it like
> self.print_internal_date(), because self would get passed as first
> argument, yes?

Unless you make it a staticmethod.

> I checked that print_internal_date can be called on an instance, so the
> same method

s/method/function/


> can be seen as:
>
> - class method -- when used in local scope definition like tagdata, or
> - instance method -- when called from an instance or from self?

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'])


So, why is it that type(Foo.bar) != type(Foo.__dict__['bar']) ? 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)
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), 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.

A (naive) implementation of the whole thing might look like this:

class method(object):
def __init__(self, func, instance, cls):
self.im_func = func
self.im_self = instance
self.im_class = cls

def __call__(self, *args, **kw):
# XXX : all sanity checks removed for readability
if self.im_self:
args = (self.im_func,) + args
return self.im_func(*args, **kw)


class function(object):
# ...
def __get__(self, instance, cls):
return method(self, instance, cls)


So, what makes a function a "method" is not being defined in a class
statement's body (well, not directly at least), it's that it is an
attribute of the class. FWIW, the following code is perfectly legal:

class Foo(object):
pass

def func(obj):
print "obj is %s " % obj

Foo.method = func

f = Foo()
f.method()
Foo.method(f)
func(f)

> I wonder if I'm not trying to make Python things it shouldn't be doing,
> but it's the problem at hand that is leading me into this conundrum: all
> other functions for tagdata use single arguments. I should probably code
> around that anyway..

Well, the simple solution is to just leave print_internal_date as a
plain function instead of insisting on making it a method. Python is
100% OO in that everything is an object, but it's not a "pure" OO
language, ie it doesn't require everything to happen in a method. So if
all you need is a function, by all mean just use a function !-)

Now if you really need print_internal_date to be exposed as a method of
PYFileInfo - like, you need polymorphic dispatch - then make it a
staticmethod.

My 2 cents...

John Posner

unread,
Feb 17, 2010, 3:55:35 PM2/17/10
to Bruno Desthuilliers
On 2/17/2010 2:44 PM, Bruno Desthuilliers wrote:
>
> 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.

Very nice writeup, Bruno -- thanks!

<snip>

>
> class method(object):
> def __init__(self, func, instance, cls):
> self.im_func = func
> self.im_self = instance
> self.im_class = cls
>
> def __call__(self, *args, **kw):
> # XXX : all sanity checks removed for readability
> if self.im_self:
> args = (self.im_func,) + args

In the line above, I think you meant:

args = (self.im_self,) + args

-John

Arnaud Delobelle

unread,
Feb 17, 2010, 4:24:36 PM2/17/10
to
Bruno Desthuilliers <bdesth.qu...@free.quelquepart.fr> writes:

[...]


> class Foo(object):
> def bar(self):
> return "baaz"
>
> print Foo.__dict__.keys()
> print type(Foo.__dict__['bar'])
>
>
> So, why is it that type(Foo.bar) != type(Foo.__dict__['bar']) ? The
> answer is : attribute lookup rules and the descriptor protocol.

It's true of Python 2.X. In Python 3 there are no more unbound method:

Python 3.2a0 (py3k:75274, Oct 7 2009, 20:25:52)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> class A:
... def f(self): pass
...
>>> A.f
<function f at 0x445b28>
>>> A.f is A.__dict__['f']
True

--
Arnaud

Bruno Desthuilliers

unread,
Feb 17, 2010, 3:34:51 PM2/17/10
to
John Posner a écrit :

> On 2/17/2010 2:44 PM, Bruno Desthuilliers wrote:
>>
> Very nice writeup, Bruno -- thanks!

<blush />

> <snip>


>
>>
>>
>> def __call__(self, *args, **kw):
>> # XXX : all sanity checks removed for readability
>> if self.im_self:
>> args = (self.im_func,) + args
>
> In the line above, I think you meant:
>
> args = (self.im_self,) + args

oops :?

Yes, indeed. Thanks for the correction.

Ben Finney

unread,
Feb 17, 2010, 5:04:47 PM2/17/10
to
Bruno Desthuilliers <bdesth.qu...@free.quelquepart.fr> writes:

> 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.

[…]

Bruno, that's the first time I've understood the descriptor protocol, or
even understood *why* the descriptor protocol is important.

Could you please post (a version of) this as a separate article
somewhere? A weblog post, or a new thread in this forum, or in the
documentation? Somewhere that web searches will find it prominently when
searching for the Python descriptor protocol.

Thank you.

--
\ “I don't want to live peacefully with difficult realities, and |
`\ I see no virtue in savoring excuses for avoiding a search for |
_o__) real answers.” —Paul Z. Myers, 2009-09-12 |
Ben Finney

Mark Lawrence

unread,
Feb 17, 2010, 5:14:07 PM2/17/10
to pytho...@python.org
Ben Finney wrote:
> Bruno Desthuilliers <bdesth.qu...@free.quelquepart.fr> writes:
>
>> 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.
> […]
>
> Bruno, that's the first time I've understood the descriptor protocol, or
> even understood *why* the descriptor protocol is important.
>
> Could you please post (a version of) this as a separate article
> somewhere? A weblog post, or a new thread in this forum, or in the
> documentation? Somewhere that web searches will find it prominently when
> searching for the Python descriptor protocol.
>
> Thank you.
>

I'll second, third and fourth this request. More please Bruno, or from
anybody similarly qualified.

Thanks very much.

Mark Lawrence.

Bruno Desthuilliers

unread,
Feb 18, 2010, 4:43:17 AM2/18/10
to
Mark Lawrence a écrit :


First, thanks Ben and Mark for the kind words...

I never tried to google for them, but there must be at least 4 previous
posts here where I tried to explain the topic. Perhaps this latest
attempt was better ?-) But anyway : if it's about making this text
easier to find, it seems that posting it here is not the most efficient
solution !-)

Anyway : I don't have a blog and don't really have enough time /
motivation for blogging. Now if anyone is willing and able to host such
a text on his own website / blog / whatever, I'd be happy to rework it
a bit.

/wrt to have it in the documentation and the "from anybody similarly
qualified", there's already a pretty comprehensive article in the
official docs (well : linked from the official docs), from someone who's
_way_ more qualified that I'll ever be - FWIW, everything I explained
here and all my knowledge of the descriptor protocol, methods etc come
from this article:

http://users.rcn.com/python/download/Descriptor.htm

But I understand it can be a bit intimidating for newcomers, so yes,
perhaps a lighter introductory text could be helpful. So guys, if you
think a revised version of my post would be of interest, I'll take you
on words: provide the hosting, I'll provide the content !-)

B.

mk

unread,
Feb 18, 2010, 4:59:40 AM2/18/10
to pytho...@python.org
Stephen Hansen wrote:
> Or just leave it as a top level function where it was perfectly happy to
> live :)

Yes. This is probably the sanest solution anyway, because probably
having many such functions to use, packing them into smth like
package.utils anyway is a good idea. I'm trying mainly to learn and
test-drive such solutions, not that I would be keen to use them
excessively in production code.

> This obviously means no other method can call it like
> self.print_internal_date(), because self would get passed as first
> argument, yes?
>
>

> It doesn't have to be. It could be a class method-- @classmethod does
> that, makes it receive 'cls' the actual class as the first argument. Or
> a @staticmethod, in which case it has no first-argument at all.

Ahh now I get it. Thanks! So that's the use for @staticmethod (I was
wondering if there was any).

Ben Finney

unread,
Feb 18, 2010, 5:36:30 AM2/18/10
to
Bruno Desthuilliers <bruno.42.de...@websiteburo.invalid> writes:

> perhaps a lighter introductory text could be helpful. So guys, if you
> think a revised version of my post would be of interest, I'll take you
> on words: provide the hosting, I'll provide the content !-)

Here, let me work my hosting magic: <URL:http://wiki.python.org/>.

--
\ “Choose mnemonic identifiers. If you can't remember what |
`\ mnemonic means, you've got a problem.” —Larry Wall |
_o__) |
Ben Finney

Bruno Desthuilliers

unread,
Feb 18, 2010, 6:20:25 AM2/18/10
to
Ben Finney a écrit :

> Bruno Desthuilliers <bruno.42.de...@websiteburo.invalid> writes:
>
>> perhaps a lighter introductory text could be helpful. So guys, if you
>> think a revised version of my post would be of interest, I'll take you
>> on words: provide the hosting, I'll provide the content !-)
>
> Here, let me work my hosting magic: <URL:http://wiki.python.org/>.

Duh ! (me banging my head on the desktop)

Ok, I'll do my part of the job then !-)

mk

unread,
Feb 18, 2010, 7:47:23 AM2/18/10
to pytho...@python.org
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)?

> 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.


> 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?

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?

Python is science, I gather: an answer to one question bears another 10
questions.

> 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
> 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!

Regards,
mk

Steve Holden

unread,
Feb 18, 2010, 8:30:35 AM2/18/10
to pytho...@python.org
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/

Bruno Desthuilliers

unread,
Feb 18, 2010, 8:34:37 AM2/18/10
to
mk a écrit :

> 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?

answered below - read on, young padawan <g>

> 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?

Indeed.

> 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 <blush />

> 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)?

Yes, but it's not bound to a Foo instance.

>> So, why is it that type(Foo.bar) != type(Foo.__dict__['bar']) ?
>
> >>> type(Foo.__dict__['bar'])
> <type 'function'>

Yeps. That's the function object created by the def statement. Just a
plain old function - expect it's an attribute of class object "Foo".

> >>> type(Foo.bar)
> <type 'instancemethod'>
>
> instancemethod - now that's something new.

Don't let the "<unbound method Foo.bar>" fools you - it's just
instancemethod.__repr__ that issues different wording according to
whether the instancemethod instance is bound or not.

>
>> 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?

s/call/lookup/

If it's looked up on the class, there's no instance to pass to __get__.

>
> 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?

Having access to the class is handy when you don't have the instance.
The point is mostly to let the descriptor know how it has been looked up
and take appropriate decisions based on this - for a definition of
"appropriote" that depends on what the descriptor is intended for .
Remember that this mechanism provides the generic support for all kind
of computed attributes - methods, properties, and whatever you can write
yourself.

> Python is science, I gather: an answer to one question bears another 10
> questions.

That's the case with most technical domains - until you solved enough of
the puzzle to start and see the big picture.

>> 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?

In that case, it's quite simple: function.__get__ is a method of the
function type, so it's called with 'self' as first argument !-)

> Aside from rejected
> PEP (http://www.python.org/dev/peps/pep-3130/) I don't see the way of
> accessing itself outside globals()

You're confusing the function instance itself with the content of the
def statement's block. The code within the def statement's block has no
access to the function instance that will be built from it, but other
methods of the function instance are, well, ordinary methods.

> (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)

it receives itself, yes. Ordinary method call, nothing magical here -
well, almost...!-).

> as an argument, like in
> your example implementation below?

Exactly.

>> 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.

Now you know. As far as I'm concerned, I do find this whole mechanism to
be a thing of beauty - instead of special-casing methods, you just use
plain functions and the much more generic descriptor protocol to achieve
the same result - with much more flexibility.

> !!THANKS!!
>
>> My 2 cents...
>
> Well, Bruno -- that was more like $200!

Ok, make it 3 cents then !-)

More seriously, all this is explained in Raymond Hettinger's excellent
'descriptor howto' article, that is linked from the official doc - and
that's how I learned all this.

0 new messages