【问题标题】:add methods in subclasses within the super class constructor在超类构造函数的子类中添加方法
【发布时间】:2011-02-13 05:46:34
【问题描述】:

我想将方法​​(更具体地说:方法别名)自动添加到 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'

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

【问题讨论】:

    标签: python oop inheritance constructor introspection


    【解决方案1】:

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

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

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

    请注意该方法是如何存在于键 GET 下的,但不在 get 下。 GET 是实例属性,但 get 不是。这在许多方面有细微的不同:该方法在类对象中不存在,因此您不能使用Sub.GET(instance) 来调用SubGET 方法,即使您可以使用Sub.get(instance) .其次,如果您有一个 Sub 的子类定义了自己的 GET 方法但没有定义自己的 get 方法,则实例属性将隐藏子类 GET 方法 使用来自基类的绑定 get 方法。第三,它在绑定方法和实例之间创建循环引用:绑定方法具有对实例的引用,而实例现在存储对绑定方法的引用。通常绑定的方法不存储在实例上,部分是为了避免这种情况。循环引用通常不是什么大问题,因为我们现在有 cyclic-gc 模块 (gc) 来处理它们,但它不能总是清理引用循环(例如,当你的类也有一个 @ 987654337@ 方法。)最后,存储绑定方法对象通常会使您的实例不可序列化:大多数序列化程序(例如pickle)无法处理绑定方法。

    您可能不关心这些问题,但如果您关心,那么有一种更好的方法可以解决您正在尝试做的事情:元类。您可以在创建类时将普通函数分配给类属性,而不是在创建实例时将绑定方法分配给实例属性:

    class 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.getSub.GET 确实是别名,在子类中覆盖一个而不是另一个可以按预期工作。

    >>> Sub.get
    <unbound method Sub.get>
    >>> Sub.GET
    <unbound method Sub.get>
    >>> Sub().get
    <bound method Sub.get of <__main__.Sub object at 0xb708978c>>
    >>> Sub().GET
    <bound method Sub.get of <__main__.Sub object at 0xb7089a6c>>
    >>> Sub().__dict__
    {}
    

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

    【讨论】:

      【解决方案2】:

      因为类 Sub 还没有被启动,所以在它的实例中做就像

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

      【讨论】:

        【解决方案3】:

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

        【讨论】:

        • 那我再重复一遍。但它在实例化后起作用,正如“S.Mark”所示。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-01-12
        相关资源
        最近更新 更多