How modules work in Python

Steven D'Aprano steve+comp.lang.python at pearwood.info
Wed Nov 19 12:32:46 CET 2014


Larry Hudson wrote:

> Your example may look the same (it uses the same dot syntax), but here it
> is to resolve a namespace -- a module is not an object.  So yes, this is
> still a function and not a method.  But we're getting rather pedantic
> here.


But not pedantic enough. Modules are, in fact, objects. All values in Python
are objects, including classes themselves! But modules are merely instances
of ModuleType:


py> import types
py> isinstance(types, types.ModuleType)
True
py> print(types)
<module 'types' from '/usr/local/lib/python3.3/types.py'>



The *simple* answer is that when you define a function inside the body of a
class, Python "magically" turns it into a method. But the *pedantic* answer
has to do with the descriptor protocol, and that is quite pedantic indeed.

A *simplified* description of the pedantic answer:

    When you look up an attribute, say obj.attr, Python checks 
    whether the attribute has a __get__ method. If it does, 
    rather than return that object itself, Python returns the 
    result of calling __get__. Functions have a __get__ method,
    and so they are descriptors.  The __get__ method of functions
    returns a method object.


Here's an example. Let's define a class with a function inside it, and an
instance:

py> class X(object):
...     def method(self):
...         return "spam"
... 
py> instance = X()


If we look inside the class __dict__, we can see the original function:

py> X.__dict__['method']
<function X.method at 0xb7cc814c>


If we look at that function, we see it has a __get__ method:

py> f = X.__dict__['method']
py> f.__get__
<method-wrapper '__get__' of function object at 0xb7cc814c>


Calling that __get__ method returns a method object:

py> f.__get__(instance, X)
<bound method X.method of <__main__.X object at 0xb7bd65ec>>


which is what a normal attribute lookup does:

py> instance.method
<bound method X.method of <__main__.X object at 0xb7bd65ec>>


Now, the twist: when you store an object in the *instance* __dict__, the
descriptor protocol doesn't get invoked. So when you have a function in a
module, it is stored in the instance dictionary and looking up module.func
does not call __get__ and does not return a method, it just returns the
function.

The same thing applies to non-modules too. If you define a function inside
the body of a class, it is stored in the class __dict__, but if you store
it in the instance __dict__, it never gets converted to a method.


You can read up on descriptors here:

https://docs.python.org/3/howto/descriptor.html



-- 
Steven




More information about the Python-list mailing list