Python中“返回的超对象未绑定”是什么意思?
5 个回答
"Unbound"的意思是它会返回这个类本身,而不是这个类的一个实例。
这里有几个答案提到的内容,但有一个重要的联系没有被提到。虽然有解释(有界和无界)方法的内容,并且给出了无界超对象的使用示例,但这些并没有真正解释它们之间的关系,以及无界超对象应该如何使用。
关键在于理解无界对象其实只是指描述符,它们和属性有点像。描述符是一种类变量,具有获取(getter)、设置(setter)和/或删除(deleter)方法。当实例以正确的方式使用这些描述符时,这些方法就会被调用。
那么,我们到底该如何在这种情况下使用这些描述符呢?其实我们只需要把它绑定到一个类上,像这样:
class A:
def foo(self):
print("A")
class B(A):
def foo(self):
print("B")
B.parent = super(B)
将 B.parent
设置为一个描述符,就相当于这样做:
class B(A):
pass
def foo(self):
print("B")
B.foo = foo
然后我们可以把这个功能应用到我们的实例上:
x = B()
x.foo() # B
x.parent.foo() # A
这展示了如何使用它们。实际上,在这种情况下,foo
和 parent
都是描述符。当将 foo 绑定到 x
时,它会用 x
填充第一个参数,并返回结果方法。当将 parent 绑定到 x
时,它也是这样做的,填充 super
的下一个参数。这意味着使用 x.parent
相当于使用 super(B, x)
,这就是在方法内部使用 super()
的结果。
编辑:关于super
的内容,下面的很多说法是不正确的。请查看John Y.的评论。
super(Foo, a).bar
会返回在方法解析顺序(MRO)中下一个对象的bar
方法,这里是和对象a
绑定的,a
是Foo
的一个实例。如果不写a
,那么返回的方法就是未绑定的。方法其实就是对象,但它们可以是绑定的或未绑定的。
未绑定的方法是指没有和某个类的实例关联的方法。它不会把类的实例作为隐含的第一个参数。
你仍然可以调用未绑定的方法,但需要明确地把类的实例作为第一个参数传入。
下面是一个绑定方法和未绑定方法的例子,以及如何使用它们。
In [1]: class Foo(object):
...: def bar(self):
...: print self
...:
In [2]: Foo.bar
Out[2]: <unbound method Foo.bar>
In [3]: a = Foo()
In [4]: a.bar
Out[4]: <bound method Foo.bar of <__main__.Foo object at 0x4433110>>
In [5]: a.bar()
<__main__.Foo object at 0x4433110>
In [6]: Foo.bar()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-6-bb3335dac614> in <module>()
----> 1 Foo.bar()
TypeError: unbound method bar() must be called with Foo instance as first argument (got nothing instead)
In [7]: Foo.bar(a)
<__main__.Foo object at 0x4433110>
为了简单说明“绑定/未绑定”这两个术语,我将用“函数”来举例,并且为了简洁起见,我会省略一些细节和注意事项。
我们可以从两个角度来看这个问题:
从用户的角度来看,函数可以分为三种类型:
自由函数(未绑定),比如内置的函数
sum()
:sum([1, 2, 3]) # 6
一个绑定到对象的函数(也叫做对象方法)意味着用户需要用点(
.
)符号将它绑定到特定对象上。例如使用内置的
.upper()
方法:"Alice".upper() # ALICE "Jacob".upper() # JACOB (the same method; different object, different result)
一个绑定到类的函数(也叫做类方法)意味着用户需要用点(
.
)符号将它绑定到特定类上。示例:
A.some_class_method() # A is a class B.some_class_method(parameters) # B is a class
从设计者的角度来看,函数同样可以分为这三种类型,所以我用相同的编号:
自由(未绑定)函数是在类外定义的:
def free_function(parameters): ...
一个绑定到对象的函数是在类内部定义的,第一个参数保留给对象,通常命名为
self
(这个名字只是一个约定,但非常常用):class A: def bound_to_object(self, other_parameters): ...
一个绑定到类的函数是在类内部定义的,第一个参数保留给类,通常命名为
cls
(这个名字也是一个约定,但同样非常常用),并且在它前面有@classmethod
装饰器:class A: @classmethod def bound_to_class(cls, other_parameters): ...
其他回答(回答, 回答)已经解释了“绑定”和“未绑定”这两个词的意思。
所以我这里主要是想讲讲如何使用从
super()
函数返回的 未绑定代理对象(也就是只用一个参数的情况)。
获取未绑定对象的原因是为了 稍后进行绑定。
特别是,对于从 super()
函数返回的未绑定对象,你可以使用它的 __get__()
方法将其绑定到合适的对象上,比如:
super(C).__get__(c) # where C is a class and c is an object
为了说明这一点,我们来创建三个相互依赖的类和三个对象——每个类一个对象:
class A:
def __init__(self, name):
self.name = name
def message(self, source):
print(f"From: {source}, class: A, object: {self.name}")
class B(A):
def __init__(self, name):
self.name = name
def message(self, source):
print(f"From: {source}, class: B, object: {self.name}")
class C(B):
def __init__(self, name):
self.name = name
def message(self, source):
print(f"From: {source}, class: C, object: {self.name}")
a = A("a")
b = B("b")
c = C("c")
现在在交互式控制台中,首先是为了理解这些内容:
>>> super(B) # unbounded (note 'None')
<super: __main__.B, None>
>>> super(B).__get__(b) # bounded to object b (note address)
<super: __main__.B, <__main__.B at 0xa9bdac0>>
>>> b # note: the same address
'<__main__.B at 0xa9bdac0>
然后——展示使用不同类/对象组合的结果
(在我们的例子中是为了委托方法 .message()
):
>>> super(B).__get__(b).message("super(B)")
From: super(B), class: A, object: b
>>> super(C).__get__(c).message("super(C)")
From: super(C), class: B, object: c
>>> super(B).__get__(c).message("super(B)")
From: super(B), class: A, object: c
最后是绑定和未绑定代理到合适类的示例:
>>> A.name = "Class A" # Preparing for it -
>>> B.name = "Class B" # creating some class attributes
>>> super(B).__get__(B).name # Proxy super(B) bounded to class B
'Class A'
>>> super(B).__get__(C).name # Proxy super(B) bounded to class C
'Class A'
>>> super(C).__get__(C).name # Proxy super(C) bounded to class C
'Class B'