在类上调用方法并手动传递实例是否总是等效于在实例上调用该方法

  • 本文关键字:方法 调用 实例 是否 python
  • 更新时间 :
  • 英文 :


即给定一个类和一个实例,如下所示:

class Foo:
    def bar(self):
        ...
foo = Foo()

Foo.bar(foo)是否总是等同于foo.bar(),或者是否存在前一个调用可能导致意外结果的极端情况?

> 不一定。描述符协议允许您定义将对象作为属性访问的含义。

  • Foo.bar(foo)相当于Foo.__dict__['bar'].__get__(None, Foo)
  • foo.bar()相当于type(foo).__dict__['bar'].__get__(foo, type(foo))

有两个分歧点。首先,type(foo)可能会也可能不会Foo,其次,根据__get__的定义方式,__get__收到时的结果foo vs. None,因为它的第一个参数可能会有所不同。

当装饰

Foo.bar()时,角落情况会发挥作用,例如以下两种方式之一:

class Foo:
    @classmethod
    def bar(cls, inst):
        ...
    @staticmethod
    def bar(inst):
        ...

在前一种情况下,调用 Foo.bar() 会自动将类Foo作为参数传递 - 不是类的实例,而是类本身。如果你要做一个子类Bar,那么Bar.bar()会将类Bar作为参数传递。这对于多态性很有用。

在后一种情况下,没有传递隐式参数。您仍然必须通过其类(Foo.bar()(调用该方法,但是调用该方法的实例(如果出于某种原因这样做(和调用它的类都不会作为参数传递。


但是,通常,Foo.bar(foo)等效于foo.bar() - 类中方法的默认行为是接受调用该方法的实例作为该方法的第一个参数。

当你执行foo.bar时,它首先尝试在foo本身上查找bar。由于bar是在类Foo而不是实例foo中定义的,因此它将失败。只有这样,它才会继续前进并尝试在foo.__class__上查找bar(即 Foo(,并成功。我们可以利用这一点:

class Foo:
    def bar(self):
        ...
foo = Foo()
foo.bar = lambda: print('hello')

现在,foo.bar()Foo.bar(foo)会产生不同的操作,因为在这两种情况下将解决不同的功能。

最新更新