What is the difference between a function, an unrelated method, and a related method?

I am asking this question due to the discussion of the comment flow of this answer . I'm 90% off bowing my head.

In [1]: class A(object): # class named 'A' ...: def f1(self): pass ...: In [2]: a = A() # an instance 

f1 exists in three different forms:

 In [3]: a.f1 # a bound method Out[3]: <bound method a.f1 of <__main__.A object at 0x039BE870>> In [4]: A.f1 # an unbound method Out[4]: <unbound method A.f1> In [5]: a.__dict__['f1'] # doesn't exist KeyError: 'f1' In [6]: A.__dict__['f1'] # a function Out[6]: <function __main__.f1> 

What is the difference between a related method, an unconnected method, and function objects, all of which are described by f1? What can be called these three objects? How can they be transformed into each other? The documentation on this is pretty hard to understand.

+55
function python methods oop
Aug 14 2018-12-12T00:
source share
6 answers

The function is created by the def or lambda operator. In Python 2, when a function appears in the body of a class statement (or is passed to a call to the construction of a type class), it is converted to an unbound method. (Python 3 has no unrelated methods, see below.) When a class instance is accessed by a function, it is converted to the bound method, which automatically passes the instance to the method as the first parameter of self .

 def f1(self): pass 

Here f1 is a function.

 class C(object): f1 = f1 

C.f1 is now an unrelated method.

 >>> C.f1 <unbound method C.f1> >>> C.f1.im_func is f1 True 

We can also use the constructor of the type class:

 >>> C2 = type('C2', (object,), {'f1': f1}) >>> C2.f1 <unbound method C2.f1> 

We can convert f1 to an unbound method manually:

 >>> import types >>> types.MethodType(f1, None, C) <unbound method C.f1> 

Unrelated methods are bound by access to the class instance:

 >>> C().f1 <bound method C.f1 of <__main__.C object at 0x2abeecf87250>> 

Access is converted to a call through the descriptor protocol:

 >>> C.f1.__get__(C(), C) <bound method C.f1 of <__main__.C object at 0x2abeecf871d0>> 

Combining them:

 >>> types.MethodType(f1, None, C).__get__(C(), C) <bound method C.f1 of <__main__.C object at 0x2abeecf87310>> 

Or directly:

 >>> types.MethodType(f1, C(), C) <bound method C.f1 of <__main__.C object at 0x2abeecf871d0>> 

The main difference between a function and an unrelated method is that the latter knows which class it is attached to; calling or binding an unbound method requires an instance of its class type:

 >>> f1(None) >>> C.f1(None) TypeError: unbound method f1() must be called with C instance as first argument (got NoneType instance instead) >>> class D(object): pass >>> f1.__get__(D(), D) <bound method D.f1 of <__main__.D object at 0x7f6c98cfe290>> >>> C.f1.__get__(D(), D) <unbound method C.f1> 

Because the difference between a function and an unbound method is pretty minimal, Python 3 gets rid of the difference; under Python 3, accessing a function in an instance of a class simply gives you the function itself:

 >>> C.f1 <function f1 at 0x7fdd06c4cd40> >>> C.f1 is f1 True 

In both Python 2 and Python 3, these three are equivalent:

 f1(C()) C.f1(C()) C().f1() 

Binding a function to an instance has the effect of fixing its first parameter (conditionally called self ) to the instance. Thus, the associated method C().f1 equivalent to any of:

 (lamdba *args, **kwargs: f1(C(), *args, **kwargs)) functools.partial(f1, C()) 
+60
Aug 14 2018-12-12T00:
source share

quite hard to understand

Well, this is a pretty complicated topic, and it is related to descriptors.

Let's start with the function. Everything is clear here - you just call it, all provided arguments are passed when it is executed:

 >>> f = A.__dict__['f1'] >>> f(1) 1 

The usual TypeError occurs with any task with the number of parameters:

 >>> f() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: f1() takes exactly 1 argument (0 given) 

Now, the methods. Methods are functions with a little spice. There are descriptors here. As described in the Data Model , A.f1 and A().f1 translated into A.__dict__['f1'].__get__(None, A) and type(a).__dict__['f1'].__get__(a, type(a)) respectively. And the results of these __get__ are different from the raw f1 function. These objects are wrappers around the original f1 and contain some additional logic.

In the case of the unbound method this logic involves checking whether the first argument is an instance of A :

 >>> f = A.f1 >>> f() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unbound method f1() must be called with A instance as first argument (got nothing instead) >>> f(1) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unbound method f1() must be called with A instance as first argument (got int instance instead) 

If this check succeeds, it executes the original f1 with this instance as the first argument:

 >>> f(A()) <__main__.A object at 0x800f238d0> 

Note that the im_self attribute im_self None :

 >>> f.im_self is None True 

In the case of the bound method this logic immediately sends the original f1 instance of A that was created (this instance is actually stored in the im_self attribute):

 >>> f = A().f1 >>> f.im_self <__main__.A object at 0x800f23950> >>> f() <__main__.A object at 0x800f23950> 

So, bound means that the underlying function is bound to some instance. unbound means it is still connected, but only with the class.

+7
Aug 14 '12 at 10:25
source share

Function object - the called object created by the function definition. Both related and unrelated methods are callable objects created by a descriptor called by a binary point operator.

Bound and unbound method objects have 3 main properties: im_func is a function object defined in a class, im_class is a class, and im_self is an instance of the class. For unbound methods, im_self is None .

When the associated method is called, it calls im_func with im_self , since the first parameter follows its calling parameters. unbound methods calls the base function only with its calling parameters.

+3
Aug 14 2018-12-12T00:
source share

One interesting thing that I saw today is that when I assign a function to a member of the class, it becomes an unrelated method. For example:

 class Test(object): @classmethod def initialize_class(cls): def print_string(self, str): print(str) # Here if I do print(print_string), I see a function cls.print_proc = print_string # Here if I do print(cls.print_proc), I see an unbound method; so if I # get a Test object o, I can call o.print_proc("Hello") 
+2
Nov 15 '14 at 6:45
source share

Refer to Python 2 and Python 3 for more details.

My interpretation is as follows.

Function class fragments:

Python 3:

 class Function(object): . . . def __get__(self, obj, objtype=None): "Simulate func_descr_get() in Objects/funcobject.c" if obj is None: return self return types.MethodType(self, obj) 

Python 2:

 class Function(object): . . . def __get__(self, obj, objtype=None): "Simulate func_descr_get() in Objects/funcobject.c" return types.MethodType(self, obj, objtype) 
  • If a function is called without a class or instance, this is a simple function.
  • If a function is called from a class or instance, its __get__ is called to retrieve the wrapped function:
    but. Bx same as B.__dict__['x'].__get__(None, B) . In Python 3, this returns a regular function. In Python 2, this returns an unrelated function.

    b. Bx same as type(b).__dict__['x'].__get__(b, type(b) . This will return the associated method in both Python 2 and Python 3, which means that self will be implicitly passed as the first argument.

+2
May 22 '18 at 8:00
source share

What is the difference between a function, an unbound method, and a related method?

From a functional perspective, there is no difference. Python's object-oriented functions are based on a functional environment.

Being connected is equal to:

Will the function accept a class ( cls ) or an object instance ( self ) as the first parameter or not?

Here is an example:

 class C: #instance method def m1(self, x): print(f"Excellent m1 self {self} {x}") @classmethod def m2(cls, x): print(f"Excellent m2 cls {cls} {x}") @staticmethod def m3(x): print(f"Excellent m3 static {x}") ci=C() ci.m1(1) ci.m2(2) ci.m3(3) print(ci.m1) print(ci.m2) print(ci.m3) print(C.m1) print(C.m2) print(C.m3) 



Outputs:

 Excellent m1 self <__main__.C object at 0x000001AF40319160> 1 Excellent m2 cls <class '__main__.C'> 2 Excellent m3 static 3 <bound method C.m1 of <__main__.C object at 0x000001AF40319160>> <bound method C.m2 of <class '__main__.C'>> <function C.m3 at 0x000001AF4023CBF8> <function C.m1 at 0x000001AF402FBB70> <bound method C.m2 of <class '__main__.C'>> <function C.m3 at 0x000001AF4023CBF8> 

The output shows that the static function m3 will never be called bound. C.m2 is associated with class C because we sent the cls parameter which is the class pointer.

ci.m1 and ci.m2 both connected; ci.m1 because we sent self which is a pointer to the instance, and ci.m2 because the instance knows that the class is bound;).

In conclusion, you can bind a method to a class or to an object of a class based on the first parameter that the method takes. If the method is not bound, it can be called unbound.




Note that a method cannot be originally part of a class. Check out this answer from Alex Martelli for more details.

0
May 24 '19 at 14:49
source share



All Articles