Python中“返回的超对象未绑定”是什么意思?

16 投票
5 回答
3705 浏览
提问于 2025-04-17 22:17

根据这个链接

如果第二个参数省略了,返回的super对象就是“未绑定”的。

这就是super(type)的意思。

我想知道什么是“未绑定”,以及什么情况下它是“绑定”的。

5 个回答

-4

"Unbound"的意思是它会返回这个类本身,而不是这个类的一个实例。

0

这里有几个答案提到的内容,但有一个重要的联系没有被提到。虽然有解释(有界和无界)方法的内容,并且给出了无界超对象的使用示例,但这些并没有真正解释它们之间的关系,以及无界超对象应该如何使用。

关键在于理解无界对象其实只是指描述符,它们和属性有点像。描述符是一种类变量,具有获取(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

这展示了如何使用它们。实际上,在这种情况下,fooparent 都是描述符。当将 foo 绑定到 x 时,它会用 x 填充第一个参数,并返回结果方法。当将 parent 绑定到 x 时,它也是这样做的,填充 super 的下一个参数。这意味着使用 x.parent 相当于使用 super(B, x),这就是在方法内部使用 super() 的结果。

0

编辑:关于super的内容,下面的很多说法是不正确的。请查看John Y.的评论。

super(Foo, a).bar会返回在方法解析顺序(MRO)中下一个对象的bar方法,这里是和对象a绑定的,aFoo的一个实例。如果不写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>
1

为了简单说明“绑定/未绑定”这两个术语,我将用“函数”来举例,并且为了简洁起见,我会省略一些细节和注意事项。

我们可以从两个角度来看这个问题:


  1. 用户的角度来看,函数可以分为三种类型:

    1. 自由函数(未绑定),比如内置的函数 sum()

      sum([1, 2, 3])          # 6
      
    2. 一个绑定到对象的函数(也叫做对象方法)意味着用户需要用点(.)符号将它绑定到特定对象上。

      例如使用内置的 .upper() 方法:

      "Alice".upper()     # ALICE
      "Jacob".upper()     # JACOB (the same method; different object, different result)
      
    3. 一个绑定到类的函数(也叫做类方法)意味着用户需要用点(.)符号将它绑定到特定类上。

      示例:

      A.some_class_method()               # A is a class
      B.some_class_method(parameters)     # B is a class
      

  1. 设计者的角度来看,函数同样可以分为这三种类型,所以我用相同的编号:

    1. 自由(未绑定)函数是在类外定义的:

      def free_function(parameters):
          ...
      
    2. 一个绑定到对象的函数是在类内部定义的,第一个参数保留给对象,通常命名为 self(这个名字只是一个约定,但非常常用):

      class A:
          def bound_to_object(self, other_parameters):
              ...
      
    3. 一个绑定到类的函数是在类内部定义的,第一个参数保留给类,通常命名为 cls(这个名字也是一个约定,但同样非常常用),并且在它前面有 @classmethod 装饰器:

      class A:
          @classmethod
          def bound_to_class(cls, other_parameters):
              ...
      
5

其他回答(回答, 回答)已经解释了“绑定”和“未绑定”这两个词的意思。

所以我这里主要是想讲讲如何使用从 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'

撰写回答