更改方法定义

时间:2015-12-01 15:22:45

标签: python inheritance

我想更改类的方法定义。 这是我的情况:

(我从其他文件导入这些类)

class A(object):
    def __init__(self, str):
        self.str = str

    def method_a(self):
        print self.str


class B(object):
    def __init__(self, str):
        self.a = A(str)

    def method_b(self):
        self.a.method_a()

#######################################

from module import A, B

def main():
    b = B('hello')

    def my_method_a(self):
        print self.str + 'other definition'

    b.a.method_a = my_method_a
    b.method_b()

if __name__ == '__main__':
    main()

当我尝试执行它时,我得到:

my_method_a() takes exactly 1 argument (0 given)

因为它没有“自我”。

请帮助。

2 个答案:

答案 0 :(得分:2)

如果您在修补方法之前运行type(b.a.method_a),则会看到<type 'instancemethod'>。在修补程序生成<type 'function'>之后运行相同的代码。为了使函数作为方法正常工作,它必须是的属性,而不是类的实例。以下方法可行,因为您手动调用从函数生成方法的魔法:

b.a.method_a = my_method_a.__get__(b.a, A)

有关详细信息,请参阅https://wiki.python.org/moin/FromFunctionToMethod

不同之处在于,在修补程序后调用b.a.method_a()时,method_a实例 b.a的属性,而不是类的属性 A。因此,永远不会调用函数的__get__方法来生成instancemethod对象,该对象已经b.a绑定到method_a的第一个参数。

从一个角度来看,b.a.method_a()A.method_a(b.a)相同。 Python如何实现这种转变?您需要了解描述符协议。所有函数对象都实现__get__方法以返回instancemethod对象,您可以将其视为原始函数,并将第一个参数绑定到相应的对象。请考虑以下代码:

b = B()
b.a.method_a()
  1. b是否有名为a的属性?是;我们在B.__init__
  2. 中设置了它
  3. b.a是否有method_a属性?否。
  4. type(b.a)(即A)是否有属性method_a?是。
  5. 调用A.method_a.__get__(b.a, A),因为查找了method_a个实例。结果是一个实例方法对象,其第一个参数绑定到b.a。 (这就是为什么你可以认为b.a.method_a()A.method_a(b.a)相同。)
  6. 使用零参数调用生成的实例方法。
  7. 现在考虑一下这段代码。

    b = B()
    b.a.method_a = my_method_a
    b.a.method_a()
    
    1. b是否有名为a的属性?是;我们在B.__init__
    2. 中设置了它
    3. b.a是否有method_a属性? 即可。我们在尝试调用它之前设置它。
    4. 由于b.a.method_a是实例查找而不是类查找,因此即使b.a.method_a.__get__具有my_method_a函数,也不会调用描述符协议并且不会调用__get__就像其他所有功能一样。
    5. 使用零参数调用b.a.method_a

      这会产生错误,因为函数需要一个参数。

答案 1 :(得分:0)

为什么不直接使用继承和方法覆盖:

from module import A, B

class myA(A):
    def method_a(self):
        print self.str + ' other definition'

class myB(B):
    def __init__(self, str):
        self.a = myA(str)

def main():
    b = myB('hello')
    b.method_b()

if __name__ == '__main__':
    main()