如何“窃取”或将方法从一个类复制到另一个类?
示例代码:
class A(object):
def foo(self):
print "blah"
class B(object):
foo = A.foo
B().foo()
预期输出:
"blah"
反而:
TypeError:必须以 A 实例作为第一个参数调用未绑定的方法 foo()(什么都没有)
使用__func__
:
>>> A.foo
<unbound method A.foo>
>>> A.foo.__func__
<function foo at 0x00BC5F70>
>>> class B(object):
... foo = A.foo.__func__
...
>>> B().foo()
"blah"
引用文档:
实例方法对象结合了一个类、一个类实例和任何可调用对象(通常是用户定义的函数)。
特殊的只读属性:__self__ 是类实例对象,__func__ 是函数对象;__doc__ 是方法的文档(与 __func__.__doc__ 相同);__name__ 是方法名(与 __func__.__name__ 相同);__module__ 是定义该方法的模块的名称,如果不可用,则为 None。
这里的问题是它是您试图窃取的绑定方法,但是,您的示例不涉及使用实例状态 ( self
) 的函数。因此,您有两个直接的选择:
A.foo
静态方法(@staticmethod
装饰器)functools
.例如。
import functools
stolen = functools.partial(A.foo, None)
这是有效的,因为您的方法不使用实例状态,并且不需要创建子类。
为了进一步修饰,绑定实例方法(如A.foo
)需要绑定实例参数(self
,其中 self 是 的实例A
)。在正常使用中,第一个参数是自动传递的:
a = A()
现在:
a.foo()
A.foo(a)
...都是等价的。在第一种情况下,语法从词汇的角度instance.bound_method()
推断(解析为)。这就是调用会导致错误的原因,因为它需要一个.InstanceClass.bound_method(instance)
instance
self
A.foo()
A
上面的解决方案所做的是将函数变形为将 aNone
作为实例传递的函数,因为无论如何都不会使用该实例(没有基于状态的逻辑)。在使用静态方法的情况下,它会删除第一个隐含的预期绑定实例参数self
。
您可以在此处使用类继承。继承允许您基于另一个对象创建一个对象,继承其所有功能和属性。
在这种情况下,它看起来像:
class A(object):
def foo(self):
print "blah"
class B(A):
# You can add new methods or attributes here,
# or even overwrite those inherited from A if you
# really want to, though you have to be careful with that.
pass
在那次声明之后,
>>> B().foo()
"blah"
这有效,因为:
A
,并为它创建了一个方法foo
。B
继承自 的类A
,这意味着当A
“诞生它”时,它就B
拥有了所有的东西A
。
B
是 的精确副本A
,因为我们没有对它做任何其他事情。但是,我们可以进行更改或添加更多方法。一个例子:
class A(object):
def foo(self):
print "blah"
class B(A):
def newfoo(self):
print "class A can't do this!"
在使用中,我们会看到:
>>> A().foo()
blah
>>> B().foo()
blah
>>> A().newfoo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'newfoo'
>>> B().newfoo()
class A can't do this!
特别是,您上面的代码不起作用的原因是,当您尝试设置时B.foo
,您写了
class B(object):
foo = A.foo
代替
class B(object):
foo = A().foo
当您在A.foo
没有 的情况下编写时,您是直接从type()
请求方法,这在 Python 中是行不通的。如果你要这样做,你要做的是实例化一个对象,然后获取其方法的副本,然后分配它。 A
foo = A().foo
A
foo
它在 Python 3 中有效,因为未绑定的方法已被删除(参见 Guido van Rossum 的文章)。
但是请注意,未绑定的方法是一个函数包装器,用于隐式检查第一个传递的参数是否是定义该方法的类的实例,这是通常不应违反的方法的先决条件。错误消息为您提供了正确的解决方案,即继承:
class B(A):
pass
一般来说,您不应该从非基类中窃取方法,因为它会中断非基类中的super()
调用(简称super(__class__, self)
)。确实,这个程序:
class A(object):
def foo(self):
print "blah"
super().__init__()
class B(object):
foo = A.foo.__func__
B().foo()
提出:
TypeError: super(type, obj): obj must be an instance or subtype of type
这是因为super(__class__, self)
首先检查 thatself
是 的实例(或子类型)__class__
,即A
。然而你传递了一个B
实例,B
而不是A
. 因此 Python 2 未绑定方法的错误消息很有帮助。