在超类构造函数中的子类中添加方法

时间:2010-04-28 08:15:49

标签: python oop inheritance constructor introspection

我想自动向Python子类添加方法(更具体地说:方法别名)。如果子类定义了一个名为'get'的方法,我想在子类的字典中添加一个方法别名'GET'。

不重复自己我想在基类中定义这个修改例程。但是如果我检查基类__init__方法,则没有这样的方法,因为它是在子类中定义的。一些源代码会更清楚:

class Base:

    def __init__(self):
        if hasattr(self, "get"):
            setattr(self, "GET", self.get)


class Sub(Base):

    def get():
        pass


print(dir(Sub))

输出:

['__doc__', '__init__', '__module__', 'get']

它还应包含'GET'

有没有办法在基类中做到这一点?

3 个答案:

答案 0 :(得分:2)

因为尚未启动类Sub,所以在其实例中执行

>>> s=Sub()
>>> dir(s)
['GET', '__doc__', '__init__', '__module__', 'get']
>>>

答案 1 :(得分:2)

您班级的__init__方法会将绑定方法添加为您班级的实例的属性。这与将属性添加到类不完全相同。通常,方法通过将函数存储在类中作为属性,然后创建方法对象来工作,因为这些函数作为属性从类中检索(创建仅知道它们所属的类的未绑定方法)或实例(创建绑定方法,知道它们的实例。)

这与你正在做的有什么不同?好吧,您分配了特定实例的GET 实例属性,而不是类。绑定方法成为实例数据的一部分:

>>> s.__dict__
{'GET': <bound method Sub.get of <__main__.Sub object at 0xb70896cc>>}

请注意该方法在密钥GET下的位置,但不在get下。 GET是实例属性,但get不是。这在很多方面略有不同:该方法在类对象中不存在,因此您不能Sub.GET(instance)调用Sub的{​​{1}}方法,即使你可以做GET。其次,如果你有Sub的子类定义了自己的Sub.get(instance)方法而不是它自己的GET方法,那么实例属性将隐藏子类{{1使用来自基类的绑定get方法的方法。第三,它在绑定方法和实例之间创建循环引用:bound方法具有对实例的引用,实例现在存储对绑定方法的引用。通常绑定的方法不会部分地存储在实例上以避免这种情况。循环引用通常不是一个大问题,因为我们现在有了循环gc模块(GET)来处理它们,但它不能总是清理引用循环(例如,当你的类也有一个get方法。)最后,存储绑定的方法对象通常会使您的实例不可序列化:大多数序列化程序(如gc)无法处理绑定的方法。

您可能不关心这些问题,但如果您这样做,那么您可以采用更好的方法来处理您的问题:元类。您可以在创建实例时将绑定方法分配给实例属性,而不是在创建类时将常规函数分配给类属性:

__del__

现在,pickleclass MethodAliasingType(type): def __init__(self, name, bases, attrs): # attrs is the dict of attributes that was used to create the # class 'self', modifying it has no effect on the class. # So use setattr() to set the attribute. for k, v in attrs.iteritems(): if not hasattr(self, k.upper()): setattr(self, k.upper(), v) super(MethodAliasingType, self).__init__(name, bases, attrs) class Base(object): __metaclass__ = MethodAliasingType class Sub(Base): def get(self): pass 实际上是别名,并且在子类中覆盖一个而不是另一个按预期工作。

Sub.get

(当然,如果你不希望覆盖一个而不是另一个工作,你可以简单地在你的元类中犯这个错误。)你可以做同样的事情使用类装饰器的元类(在Python 2.6及更高版本中),但这意味着需要在Base的每个子类上使用类装饰器 - 类装饰器不会被继承。

答案 2 :(得分:-1)

在派生类中创建一个派生构造函数,用于设置属性。