If a module is an object from the class 'module' why aren't regular functions considered methods?

Begginer here trying to understand how python works and i came up with this doubt.

Shouldn’t all functions be methods? Here’s the code i used to verify this

my_module.py:

def func():
    pass

Main.py:

import inspect
import my_module

print(inspect.ismethod(my_module.func))

Output: False

>Solution :

Because methods are defined on the class. Any given module is an instance of the class module, not the definition of a new class.

Compare to:

class Foo:
    pass

f = Foo()
f.stuff = lambda: print('stuff')

f.stuff is an instance attribute of that particular instance of Foo, not of Foos in general. The descriptor protocol (invoked when looking up an attribute on an instance, and finding it on the class, not the instance) is what converts functions on classes into methods of instances, and since these functions on a given module are all instance attributes of the module object, not the root class it’s an instance of, no descriptor protocol is invoked, and the function doesn’t become a method when called.

All that said, you can make subclasses of the module type, which would allow a module to have real methods. This is documented under Customizing module attribute access as a fallback in case the special methods they explicitly added support for (e.g. __getattr__, __dir__) aren’t enough and you need your module to do other things. Since the __class__ of a module is reassignable, you can reassign it to some other subclass of the module class and the methods of that class will receive self implicitly, so the same subclass can be assigned to multiple modules and provide per-module tailoring of behavior. For example:

import sys
from types import ModuleType

class MethodModule(ModuleType):
    def dumb_method(self, x):
        return f"This is module named {self.__name__} which received {x!r}"

thismodule = sys.modules[__name__]
thismodule.__class__ = MethodModule

print(thismodule.dumb_method("foo"))

Try it online!

gives the module it’s run within an instance method named dumb_method which customizes its behavior based on the self implicitly passed to it (in this case, using self.__name__ to determine the name of the module).

Leave a Reply