我有一个类:
class Parent(object):
def __init__(self, a1 = None, a2 = None):
someInitialization()
self.a1 = a1
self.a2 = a2
def coolMethod(self):
return dosomething(self.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"
答案 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
使用a1
和a2
作为属性,它必须同时实现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)