Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Different way to create an instance method object in Python

The Python 3.x language reference described two ways to create a method object:

User-defined method objects may be created when getting an attribute of a class (perhaps via an instance of that class), if that attribute is a user-defined function object or a class method object.

When an instance method object is created by retrieving a user-defined function object from a class via one of its instances, its self attribute is the instance, and the method object is said to be bound. The new method’s func attribute is the original function object.

When a user-defined method object is created by retrieving another method object from a class or instance, the behaviour is the same as for a function object, except that the func attribute of the new instance is not the original method object but its func attribute.

When an instance method object is called, the underlying function (func) is called, inserting the class instance (self) in front of the argument list. For instance, when C is a class which contains a definition for a function f(), and x is an instance of C, calling x.f(1) is equivalent to calling C.f(x, 1).

When an instance method object is derived from a class method object, the “class instance” stored in self will actually be the class itself, so that calling either x.f(1) or C.f(1) is equivalent to calling f(C,1) where f is the underlying function.

In different ways, they both have different __func__ and __self__ values, but I'm not very aware of these two different ways, could someone explain it to me?

Python Language Reference | The Standard Type Hierarchy: https://docs.python.org/3/reference/datamodel.html#the-standard-type-hierarchy

like image 511
Xu Zhoufeng Avatar asked Mar 12 '23 06:03

Xu Zhoufeng


2 Answers

I'm not 100% sure that I completely understand your question, but maybe looking at an example will be helpful. To start, lets create a class which has a function in the definition:

>>> class Foo(object):
...   def method(self):
...     pass
... 
>>> f = Foo()

User-defined method objects may be created when getting an attribute of a class (perhaps via an instance of that class), if that attribute is a user-defined function object or a class method object.

Ok, so we can create a method object by just accessing the attribute on an instance (if the attribute is a function). In our setup, f is an instance of the class Foo:

>>> type(f.method)
<class 'method'>

Compare that with accessing the method attribute on the class:

>>> type(Foo.method)
<class 'function'>

When an instance method object is created by retrieving a user-defined function object from a class via one of its instances, its __self__ attribute is the instance, and the method object is said to be bound. The new method’s __func__ attribute is the original function object.

This is just telling us what attributes exist on instance methods. Let's check it out:

>>> instance_method = f.method
>>> instance_method.__func__ is Foo.method
True
>>> instance_method.__self__ is f
True

So we see that the method object has a __func__ attribute which is just a reference to the actual Foo.method function. It also has a __self__ attribute that is a reference to the instance.

When an instance method object is called, the underlying function (func) is called, inserting the class instance (self) in front of the argument list. For instance, when C is a class which contains a definition for a function f(), and x is an instance of C, calling x.f(1) is equivalent to calling C.f(x, 1).

Basically, in reference to our example above, this is just saying that If:

instance_method = f.method

Then:

instance_method(arg1, arg2)

executes the following:

instance_method.__func__(instance_method.__self__, arg1, arg2)
like image 86
mgilson Avatar answered Apr 26 '23 14:04

mgilson


For completeness and as an addendum to the excellent answer provided by @mgilson, I wanted to explain the remaining 2 paragraphs referenced in the original question.

First let's create a class with a classmethod:

>>> class Foo(object):
...   @classmethod
...   def cmethod(cls):
...     pass
... 
>>> f = Foo()

Now for the 3rd paragraph:

When a user-defined method object is created by retrieving another method object from a class or instance, the behaviour is the same as for a function object, except that the func attribute of the new instance is not the original method object but its func attribute.

This means:

>>> class_method = f.cmethod
>>> class_method.__func__ is Foo.cmethod.__func__
True
>>> class_method.__self__ is Foo
True

Note that __self__ is a reference to the Foo class. Finally, the last paragraph:

When an instance method object is derived from a class method object, the “class instance” stored in self will actually be the class itself, so that calling either x.f(1) or C.f(1) is equivalent to calling f(C,1) where f is the underlying function.

This just says that all of the following are equivalent:

>>> f.cmethod(arg1, arg2)
>>> Foo.cmethod(arg1, arg2)
>>> f.cmethod.__func__(Foo, arg1, arg2)
>>> Foo.cmethod.__func__(Foo, arg1, arg2)
>>> f.cmethod.__func__(f.cmethod.__self__, arg1, arg2)
>>> Foo.cmethod.__func__(Foo.cmethod.__self__, arg1, arg2)
like image 40
calvin Avatar answered Apr 26 '23 14:04

calvin