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