即给定一个类和一个实例,像这样:
class Foo:
def bar(self):
...
foo = Foo()
Foo.bar(foo)
始终等于foo.bar()
,还是在极端情况下前一个呼叫会导致意外结果?
答案 0 :(得分:3)
不一定。 descriptor protocol允许您定义将对象作为属性访问的含义。
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
收到None
与{{ 1}}作为第一个参数可能会有所不同。
答案 1 :(得分:0)
装饰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()
-类中方法的默认行为是接受在其上调用该方法的实例作为该方法的第一个参数。 / p>
答案 2 :(得分:0)
当您执行foo.bar
时,它首先尝试在bar
本身上查找foo
。由于bar
是在类Foo
中定义的,而不是在实例foo
中定义的,因此它将失败。只有 then 可以继续运行,并尝试在bar
(即foo.__class__
)上查找Foo
,并成功。我们可以利用这一点:
class Foo:
def bar(self):
...
foo = Foo()
foo.bar = lambda: print('hello')
现在,foo.bar()
和Foo.bar(foo)
导致动作不同,因为在两种情况下将解决不同的功能。