使用python类中的属性覆盖属性

时间:2016-03-31 14:55:48

标签: python inheritance properties

我有一个类:

class Parent(object):
    def __init__(self, a1 = None, a2 = None):
        someInitialization()
        self.a1 = a1
        self.a2 = a2

    def coolMethod(self): 
        return dosomething(self.a2)

让我们说

  • 我不想修改父类的属性
  • 我不想在初始化时提供a1和a2,因为它们计算起来很昂贵(并且父级的每个方法都不需要它们)
  • 我不想记住Parent的哪个方法需要a1或a2,它们应该正常工作

编辑我的实际约束并不是那么严格,但我也问这个问题也是为了更好地理解类的属性,方法等。

我使用的是最简单的解决方案,没有初始化继承:

class Child(Parent):
    _propertiesCache = {'a1':None,'a2':None}
    def _initProperty(self, propertyName):
        value = self._propertiesCache[propertyName]
        if value is None:
            self._propertiesCache[propertyName]=expensiveFunction(self.b, propertyName)
        return  self._propertiesCache[propertyName]

    @property
    def a1(self):
        return self._initProperty('a1')

    @property
    def a2(self):
        return self._initProperty('a2')

    def __init__(self,b):
        self.b = b
        someInitialization()

有没有办法正确调用父类的初始化?如果我使用super(Child,self).__init__(),我会获得AttributeError: can't set attribute。 我试图覆盖__new__,但我总是遗漏某些东西

class Child(Parent):
    _propertiesCache = {'a1':None,'a2':None}
    @classmethod
    def _initProperty(cls, propertyName):
        value = cls._propertiesCache[propertyName]
        if value is None:
            cls._propertiesCache[propertyName] = someTimeConsumingFunctionThatIDontWantToCallAtInitialization(propertyName)
        return  cls._propertiesCache[propertyName]

    @property
    def a1(self):
        return self._initProperty('a1')

    @property
    def a2(self):
        return self._initProperty('a2')

    def __new__(cls):
        super(Child,cls).__new__(cls)
        return cls
    def __init__(self,b):
        self.b = b
        super(Child,self).__init__(a1=self.a1, a2=self.a2)

给我:

>>c = Child();c.a1
<property at 0x3aa4228>
>>c.a1.fget(c)
"ValueOfa1"

2 个答案:

答案 0 :(得分:3)

而不是调用你的方法_initProperty来调用它__getattr__,以便每次在应该存储的正常位置找到属性时调用它(属性字典,类字典等)。 )然后第一次尝试访问该属性时,它会被初始化。

请确保不要在父初始化中设置它们,也可以仅在它们不是None时设置它们:

class Parent:
     def __init__(self,a1=None,a2=None):
         if a1 is not None:
             self.a1 = a1
         if a2 is not None:
             self.a2 = a2

为了与错误保持一致,如果属性不存在而不是让AttributeError通过,则您需要引发KeyError,并且可能会添加对该值的引用在常规属性dict中,因此每次都不需要运行__getattr__

_propertiesCache = {'a1':None,'a2':None}
def __getattr__(self, propertyName):
    if propertyName not in self._propertiesCache:
        raise AttributeError(propertyName)
    value = self._propertiesCache[propertyName]
    if value is None:
        value = self._propertiesCache[propertyName]=expensiveFunction(self.b, propertyName)
    setattr(self,propertyName,value)
    return  value

你实现这一点,你需要确保:

直到第一次使用属性时才设置该属性(此时__getattr__被使用)

答案 1 :(得分:1)

我认为,更多符合python且符合OOP约定的是不更改Parent类-它不必预料其子级将如何覆盖其参数和行为。

为了使Child使用a1a2作为属性,它必须同时实现getter(@property)和虚拟setter(@a1.setter) 。

下面的代码假定用户无法修改a1(因此,将setter实现为虚拟对象,并且使用任意值调用Parent的__init__)。属性a2的行为稍微复杂一些:可以由用户初始化或修改,但是如果其值无效(例如,此处为负),则会被内部逻辑覆盖。

class Child(Parent):    
    def __init__(self):
        super().__init__(None, -42)


    @property
    def a1(self):
        return 42

    @a1.setter
    def a1(self, value):
        """ Do nothing. Implemented to prevent Attribute Error """


    @property
    def a2(self):
        if getattr(self, '_a2', -1) < 0:
            self._a2 = self._initProperty('a2')
        return self._a2

    @a2.setter
    def a2(self, value):
        self._a2 = value

P.S。您可以将虚拟设置器减少到仅一行:

class Child(Parent):    
    # ...
    a1 = a1.setter(lambda self, value: None)
相关问题