【问题标题】:python method stealerpython方法窃取器
【发布时间】:2012-11-12 08:02:11
【问题描述】:

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

示例代码:

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


class B(object):
  foo = A.foo

B().foo()

预期输出:

"blah"

改为:

TypeError: unbound method foo() must be called with A instance as 第一个参数(什么都没有)

【问题讨论】:

  • 如果foo 是类方法而不是实例方法,这将更有意义。

标签: python oop


【解决方案1】:

它在 Python 3 中有效,因为 未绑定的方法 已被删除(参见 Guido van Rossum 的 article)。

但是请注意,未绑定方法是一个函数包装器,用于隐式检查第一个传递的参数是定义该方法的类的实例,这是通常不应违反的方法的先决条件。错误消息为您提供了正确的解决方案,即继承:

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) 首先检查self__class__ 的实例(或子类型),即A。然而你传递了一个B 实例,而B 不是A 的子类。因此 Python 2 未绑定方法的错误消息很有帮助。

【讨论】:

    【解决方案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)instance 解析为self)。这就是调用A.foo() 会导致错误的原因,因为它需要A 的实例。

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

    【讨论】:

      【解决方案3】:

      使用__func__:

      >>> A.foo
      <unbound method A.foo>
      >>> A.foo.__func__
      <function foo at 0x00BC5F70>
      >>> class B(object):
      ...   foo = A.foo.__func__
      ...
      >>> B().foo()
      "blah"
      

      引用the docs:

      实例方法对象结合了一个类、一个类实例和任何可调用对象(通常是用户定义的函数)。

      特殊的只读属性:__self__是类实例对象,__func__是函数对象; __doc__ 是方法的文档(与 __func__.__doc__ 相同); __name__ 是方法名(与 __func__.__name__ 相同); __module__ 是定义该方法的模块的名称,如果不可用,则为 None。

      【讨论】:

      • 或者,他也可以只使用A().foo 而不是A.foo.__func__
      • @jdotjdot A().foo 是一个“绑定方法”,与A 的特定实例相关联。这在(可能是简化的)示例中无关紧要 - 因为它不使用 self,并且也可以用静态/类方法替换 - 但在更复杂的场景中它会。我假设 OP 的意图,虽然没有说明,是从任意类中选择一个有用的方法(即不意味着继承自而不是 mixin)并在他自己的类中具有相同的方法,但指/作用于他的类的实例——而不是原来的实例。
      • 你是对的。我在考虑这个具体的例子,因为 self 不是必需的——但在一般情况下,你绝对是对的,A().foo 不是一个好电话。
      【解决方案4】:

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

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

      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
      • 您从A 创建了B继承,这意味着当A“诞生它”时,BA 拥有的一切一起诞生。
        • 在我们的例子中,BA 的精确副本,因为我们没有对它做任何其他事情。不过,我们可以进行更改或添加更多方法。

      一个例子:

      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 时,您是在直接从A 类型 中请求方法,这在Python 中不起作用。如果你要做foo = A().foo,你要做的是实例化一个A 对象,然后获取其方法foo 的副本,然后分配它。

      【讨论】:

        猜你喜欢
        • 2012-11-15
        • 1970-01-01
        • 2013-06-21
        • 1970-01-01
        • 1970-01-01
        • 2015-05-29
        • 1970-01-01
        • 2019-06-26
        • 1970-01-01
        相关资源
        最近更新 更多