python - call instance method using __func__

前端 未结 1 1456
生来不讨喜
生来不讨喜 2020-12-09 03:11

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         


        
相关标签:
1条回答
  • 2020-12-09 04:03

    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.

    0 讨论(0)
提交回复
热议问题