Python: calling a function as a class method

Let's start with the code:

def func(*x): print('func:', x) class ABC: def __init__(self, f): self.f1 = f def f2(*x): print('f2:', x) 

Now we will conduct some tests:

 >>> a = ABC(func) >>> a.f1(10) func: (10,) >>> a.f2(10) f2: (<__main__.ABC object at 0xb75381cc>, 10) >>> a.f3 = func >>> a.f3(10) func: (10,) >>> a.f1 <function func at 0xb74911ec> >>> a.f2 <bound method ABC.f2 of <__main__.ABC object at 0xb75381cc>> >>> a.f3 <function func at 0xb74911ec> 

Note that func is a normal function, and we make it the f1 method for the class.

We see that f2 receives an instance of the class as the first argument, but f1 and f3 are not, although all functions are called class methods. We can also see that if we call a normal function as a class method, Python does not make a related method out of it.

So, why does f1 or f3 NOT get the class instance passed to it even when we call it as a class method? And also, as Python knows, we call an external function as a method so that it does not pass an instance to it.

- EDIT -

OK, so basically what I'm doing wrong is that I bind the functions to the instance, and NOT to the class object itself. Therefore, these functions simply become attributes of the instance. We can verify this with:

 >>> ABC.__dict__ ... contents... >>> a.__dict__ {'f1': <function func at 0xb74911ec>, 'f3': <function func at 0xb74911ec>} 

Also note that this dict cannot be assigned:

 >>> ABC.__dict__['f4'] = func TypeError: 'dict_proxy' object does not support item assignment 
+4
source share
2 answers

You partially answered your question by checking the object. In Python, objects behave like namespaces, so the first attribute points to a function, and the second points to a method.

Here's how you can add a method dynamically:

 from types import MethodType def func(*x): print('func:', x) class ABC: def __init__(self, f): self.f1 = MethodType(f, self, self.__class__) def f2(*x): print('f2:', x) if __name__ == '__main__': a = ABC(func) print a.f1(10) print a.f2(10) a.f3 = MethodType(func, a, ABC) print a.f3(10) 

Note that it will bind the method to your instance, and not to the base class. For monkeypatch class ABC:

 >>> ABC.f4 = MethodType(func, None, ABC) >>> a.f4(1) ('func:', (<__main__.ABC instance at 0x02AA8AD0>, 1)) 

Monkeypatching usually frowns in Python circles, even though it is popular in other dynamic languages ​​(especially in Ruby when the language was younger).

If you ever resort to this powerful but dangerous technique, my advice is:

  • never, never override an existing class method. just don't do it.
+8
source

This is because f1 and f3 are not a class method, they are just references to the global function defined in __main__ :

 In [5]: a.f1 Out[5]: <function __main__.func> In [8]: a.f3 Out[8]: <function __main__.func> In [9]: a.f2 Out[9]: <bound method ABC.f2 of <__main__.ABC instance at 0x8ac04ac>> 

you can do something like this to make a global function a class method:

 In [16]: class ABC: def __init__(self,f): ABC.f1=f def f2(*x): print('f2',x) ....: In [17]: a=ABC(func) In [18]: a.f1(10) ('func:', (<__main__.ABC instance at 0x8abb7ec>, 10)) 
+3
source

Source: https://habr.com/ru/post/1447874/


All Articles