Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
214 views
in Technique[技术] by (71.8m points)

python - call instance method using __func__

I am new to python, and I don't quite understand the __func__ in python 2.7.

I know when I define a class like this:

class Foo:
    def f(self, arg):
        print arg

I can use either Foo().f('a') or Foo.f(Foo(), 'a') to call this method. However, I can't call this method by Foo.f(Foo, 'a'). But I accidently found that I can use Foo.f.__func__(Foo, 'a') or even Foo.f.__func__(1, 'a') to get the same result.

I print out the values of Foo.f, Foo().f and Foo.f.__func__, and they are all different. However, I have only one piece of code in definition. Who can help to explain how above code actually works, especially the __func__? I get really confused now.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

When you access Foo.f or Foo().f a method is returned; it's unbound in the first case and bound in the second. A python method is essentially a wrapper around a function that also holds a reference to the class it is a method of. When bound, it also holds a reference to the instance.

When you call an method, it'll do a type-check on the first argument passed in to make sure it is an instance (it has to be an instance of the referenced class, or a subclass of that class). When the method is bound, it'll provide that first argument, on an unbound method you provide it yourself.

It's this method object that has the __func__ attribute, which is just a reference to the wrapped function. By accessing the underlying function instead of calling the method, you remove the typecheck, and you can pass in anything you want as the first argument. Functions don't care about their argument types, but methods do.

Note that in Python 3, this has changed; Foo.f just returns the function, not an unbound method. Foo().f returns a method still, still bound, but there is no way to create an unbound method any more.

Under the hood, each function object has a __get__ method, this is what returns the method object:

>>> class Foo(object):
...     def f(self): pass
... 
>>> Foo.f
<unbound method Foo.f>
>>> Foo().f
<bound method Foo.f of <__main__.Foo object at 0x11046bc10>>
>>> Foo.__dict__['f']
<function f at 0x110450230>
>>> Foo.f.__func__
<function f at 0x110450230>
>>> Foo.f.__func__.__get__(Foo(), Foo)
<bound method Foo.f of <__main__.Foo object at 0x11046bc50>>
>>> Foo.f.__func__.__get__(None, Foo)
<unbound method Foo.f>

This isn't the most efficient codepath, so, Python 3.7 adds a new LOAD_METHOD - CALL_METHOD opcode pair that replaces the current LOAD_ATTRIBUTE - CALL_FUNCTION opcode pair precisely to avoid creating a new method object each time. This optimisation transforms the executon path for instance.foo() from type(instance).__dict__['foo'].__get__(instance, type(instance))() with type(instance).__dict__['foo'](instance), so 'manually' passing in the instance directly to the function object. This saves about 20% time on existing microbenchmarks.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...