如何解决应该如何调用用 super() 检索的可调用对象?
我注意到在这个 B.f
实现中,调用 B.f(B)
会引发一个 TypeError
:
>>> class A:
... def f(self): print('foo')
...
>>> class B(A):
... def f(self):
... super().f()
... print('bar')
...
>>> B.f(B()) # instance self argument
foo
bar
>>> B.f(B) # non-instance self argument
Traceback (most recent call last):
File "<stdin>",line 1,in <module>
File "<stdin>",line 3,in f
TypeError: f() missing 1 required positional argument: 'self'
但使用此 B.f
实现,调用 B.f(B)
有效:
>>> class A:
... def f(self): print('foo')
...
>>> class B(A):
... def f(self):
... if isinstance(self,B): super().f() # bound method call
... else: super().f(self) # function call
... print('bar')
...
>>> B.f(B()) # instance self argument
foo
bar
>>> B.f(B) # non-instance self argument
foo
bar
这是因为当 super().f
是 super(B,self).f
的实例时,types.MethodType(A.f,self)
(即 self
)检索绑定方法 B
,并检索 function A.f
否则:
>>> super(B,B()).f
<bound method A.f of <__main__.B object at 0x10e6bda90>>
>>> super(B,B).f
<function A.f at 0x10e7d6790>
所以我想知道上面两个 B.f
实现中的哪一个是惯用的。换句话说,类设计者是否应该假设类的函数将始终使用作为类实例的 self
参数调用(就像第一个 B.f
实现总是调用 {{1} }} 作为绑定方法,即 super().f
),或者他也应该处理 super().f()
参数不是 class* 的实例的情况(如调用的第二个 self
实现B.f
作为绑定方法,即 super().f
, 或 作为函数,即 super().f()
)?
* 这是可能的,因为 Python 3.0 允许高级用法,如 Guido van Rossum explained:
在 Python 3000 中,未绑定方法的概念已被删除,表达式“A.spam”返回一个普通的函数对象。事实证明,第一个参数必须是 A 的实例这一限制对诊断问题几乎没有帮助,而且常常是高级用法的障碍——有人称其为“鸭子输入自我”,这似乎是一个合适的名称。
解决方法
我想知道上面两个 B.f 实现中的哪一个是惯用的
更简单的是惯用语。
我完全看不出子类为什么要执行 isinstance(self,Subclass)
或调用方为什么要执行 B.f(B)
的充分理由。
通常,有实例方法(倾向于将 self
作为第一个参数,并且没有装饰器)、静态方法(具有 @staticmethod
装饰器)和类方法(具有@classmethod
装饰器)。您将看到这两个装饰器(静态方法、类方法)被列为 built-in functions。
当您调用 B().f()
时,由于 B()
是一个实例,因此该方法会隐式转换为一个函数,该函数的第一个参数是该实例,即 is described in the docs here。
另一方面,当您调用 B.f()
时,这不会发生;没有插入额外的参数值(same docs 也解释了这一点)。然而,由于方法 f
是用一个没有默认值的参数定义的,这就是错误“缺少 1 个必需的位置参数...”的来源。
至于代码约定,您可以添加 isinstance
检查,但通常,代码会假设用户将使用正确的类型或从正确类型的实例调用方法,如有必要;否则用户会遇到错误。这样,您的代码就可以保持简单。
b = B()
# The `self` identifier inside `B.f` will be identical in the following two calls
b.f()
B.f(b) # Would not expect users to call `f` this way unless marked as static
# The `self` identifier in `B.f` below is a class rather than an instance of the class,as in the above two cases
# Typically,you would _not_ expect users to call `f` this way.
B.f(B)
,
我刚刚通过此 A.g
实现意识到问题并非特定于使用 super()
:
>>> class A:
... def f(self): print('foo')
... def g(self):
... self.f()
... print('bar')
...
>>> A.g(A())
foo
bar
>>> A.g(A)
Traceback (most recent call last):
File "<stdin>",line 1,in <module>
File "<stdin>",line 4,in g
TypeError: f() missing 1 required positional argument: 'self'
我们通常不编写此 A.g
实现:
>>> class A:
... def f(self): print('foo')
... def g(self):
... if isinstance(self,A): self.f() # bound method call
... else: self.f(self) # function call
... print('bar')
...
>>> A.g(A())
foo
bar
>>> A.g(A)
foo
bar
但是我们应该吗?我想知道 Guido van Rossum 所指的非实例优先参数的这些“高级用法”是什么。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。