8

如何“窃取”或将方法从一个类复制到另一个类?

示例代码:

class A(object):
  def foo(self):
    print "blah"


class B(object):
  foo = A.foo

B().foo()

预期输出:

"blah"

反而:

TypeError:必须以 A 实例作为第一个参数调用未绑定的方法 foo()(什么都没有)

4

4 回答 4

7

使用__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。

于 2012-11-24T03:03:21.040 回答
2

这里的问题是它是您试图窃取的绑定方法,但是,您的示例不涉及使用实例状态 ( self) 的函数。因此,您有两个直接的选择:

  1. 定义A.foo静态方法(@staticmethod装饰器)
  2. 装饰或包装函数以传递未使用的参数。例如使用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)instanceselfA.foo()A

上面的解决方案所做的是将函数变形为将 aNone作为实例传递的函数,因为无论如何都不会使用该实例(没有基于状态的逻辑)。在使用静态方法的情况下,它会删除第一个隐含的预期绑定实例参数self

于 2012-11-24T03:07:56.970 回答
2

您可以在此处使用类继承。继承允许您基于另一个对象创建一个对象,继承其所有功能和属性。

在这种情况下,它看起来像:

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().fooAfoo

于 2012-11-24T02:59:08.493 回答
0

它在 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 未绑定方法的错误消息很有帮助。

于 2020-08-05T15:09:47.260 回答