python - 使用__func__调用实例方法

时间:2012-10-17 13:18:58

标签: python

我是python的新手,我不太了解python 2.7中的__func__

我知道当我定义这样一个类时:

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

我可以使用Foo().f('a')Foo.f(Foo(), 'a')来调用此方法。但是,我无法通过Foo.f(Foo, 'a')调用此方法。但我意外地发现我可以使用Foo.f.__func__(Foo, 'a')甚至Foo.f.__func__(1, 'a')来获得相同的结果。

我打印出Foo.fFoo().fFoo.f.__func__的值,它们都不同。但是,我在定义中只有一段代码。谁可以帮助解释上面的代码实际上是如何工作的,尤其是__func__?我现在真的很困惑。

1 个答案:

答案 0 :(得分:36)

当您访问Foo.fFoo().f时,会返回方法;它在第一种情况下是未绑定的,在第二种情况下是绑定的。 python方法本质上是一个函数的包装器,它还包含对类的引用。绑定时,它还包含对实例的引用。

当你调用方法时,它会对传入的第一个参数进行类型检查以确保它是一个实例(它必须是引用类的实例,或者该类的子类)。当绑定方法时,它将提供第一个参数,在您自己提供的未绑定方法上。

这个方法对象具有__func__属性,它只是对包装函数的引用。通过访问底层函数而不是调用方法,可以删除类型检查,并且可以将任何您想要的内容作为第一个参数传递。函数不关心它们的参数类型,但是方法可以。

请注意,在Python 3中,这已经发生了变化; Foo.f只返回函数,而不是未绑定的方法。 Foo().f返回仍然仍然绑定的方法,但无法再创建未绑定的方法。

在幕后,每个函数对象都有一个__get__ method,这就是返回方法对象的东西:

>>> 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>

这不是最有效的代码路径,因此,Python 3.7添加了一个新的LOAD_METHOD - CALL_METHOD操作码对,可以精确地替换当前的LOAD_ATTRIBUTE - CALL_FUNCTION操作码对避免每次都创建一个新的方法对象。此优化将instance.foo()的{​​{1}}的执行路径转换为type(instance).__dict__['foo'].__get__(instance, type(instance))(),因此“手动”将实例直接传递给函数对象。这样可以节省大约20%的现有微基准测试时间。