python newbie

Bruno Desthuilliers bdesth.quelquechose at free.quelquepart.fr
Sun Nov 4 11:10:01 EST 2007


Hendrik van Rooyen a écrit :
> "Bruno Desthuilliers" wrote:
> 
> 
>>functions are *not* methods of their module.
> 
> 
> Now I am confused - if I write:
> 
> result = foo.bar(param)
> 
> Then if foo is a class, we probably all agree that bar is
> a method of foo.

We probably agree that it's an attribute of foo, and that this attribute 
is callable. Chances are that the object returned by the lookup is 
either a classmethod or a staticmethod, but not necessarily, and nothing 
in the above snippet can tell us - IOW, you have to either inspect 
foo.bar or have a look at the implementation to know what foo.bar yields.

> But the same syntax would work if I had imported some 
> module as foo.

It would also work if foo was an instance an bar a function:

class Foo(object):
   def bar(self):
      print "bar", self

def baaz(param):
   return param * 2

foo = Foo()
foo.baaz = baaz

result = foo.baaz(21)

While we're at it, notice that modules are *not* classes. They are 
instances of class 'module'.

> So what's the difference ?  Why can't bar be called a method
> of foo,

Depends. If
   type(foo.bar) is types.MethodType
yields True, then you can obviously call bar a method of foo. Else, it's 
a callable attribute. FWIW, if foo is a module and Bar a class defined 
in this module, would you call foo.Bar a method of foo ?

> or is it merely a convention that classes have
> methods and modules have functions?

It's not a convention.

Ok, time for some in-depth (even if greatly simplified) explanation.

First point is that Python objects have two special attributes: __dict__ 
and __class__. The first stores the object's attributes, and the second 
a reference to the object's class (which is itself an object...). Class 
objects also have an __mro__ special attributes that stores the 
superclasses. Attributes are first looked up in the object's __dict__, 
then in the object's class __dict__, then in the superclasses __dict__'s.

Second point : there's something in Python named the "protocol 
descriptor". This protocol mandates that, when an attribute lookup is 
performed, if
1/ the attribute belongs to a class object, and
2/ have a (callable) attribute named __get__,

then this callable attribute is called with the instance (or None if the 
attribute is directly looked up on the class) as first argument and the 
class as second argument, and the lookup mechanism yields the result of 
this call instead of the attribute itself.

Third point: function objects actually implement the descriptor 
protocol, in such a way that their __get__ method returns a method 
object - in fact, either a bound (if the attribute was looked up on the 
instance) or unbound instancemethod (if the attribute was looked up on 
the class), wrapping the class, the instance (for bound instancemethods) 
and the function itself. This is this method object which is responsible 
for fetching the instance as first argument to the function when called.

There are also the classmethod and staticmethod types - used as function 
decorators-, which themselves implements the descriptor protocol in a 
somewhat different way, but I won't say more about this, since what they 
do should be somewhat obvious now.

Now if you re-read all this carefully, you'll note that (classmethods 
and staticmethods set aside), the real attribute *is* a function object. 
It's only at lookup time that the method object is created, and only if 
it's an attribute of the class. You can easily check this by yourself:

print Foo.bar
print Foo.__dict__['bar']

So if you set a function as an attribute of a non-class object, the 
descriptor protocol won't be invoked, and you'll get the function object 
itself. Which is what happens with modules (which are instances, not 
types) and functions.

As a last note: all this only applies to the so-called "new-style" 
object model introduced in Python 2.3 (IIRC). The old object model 
(usually refered to as 'old-style' or 'classic' classes) works somewhat 
differently - but with mostly similar results.

> Note that I am purposely refraining from mentioning a module
> that has a class that has a method.

You shouldn't. Every element must be observed to solve a puzzle. And 
while we're at it, you also forgot to mention the methods of the class's 
class - since classes are themselves instances of another class !-)

HTH



More information about the Python-list mailing list