即给定一个类和一个实例,如下所示:
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)
会产生不同的操作,因为在这两种情况下将解决不同的功能。