问题描述
我正在尝试创建一个Config
Python 3类,其中用户将配置参数设置为超类上的普通类属性或超类上的属性,用户可以在(sub)类的实例上设置覆盖/阴影。
我宁愿不需要用户为使用属性方法创建的属性定义setter方法。
有没有一种方法可以破坏父类中的属性集,即使它是使用属性装饰器创建的呢?
class Configuration:
param1 = 'a'
@property
def param2(self):
return self.param1 + 'x'
class Proj1(Configuration):
param3 = 'blah'
config = Proj1()
config.param2 = 'new_val'
由于超类中的属性decorate,此代码段将尝试分配给pram2
抛出AttributeError
。
有没有一种方法可以轻松破坏现有属性并使用任意属性值对其进行阴影处理?
1楼
看起来您的目标似乎是使派生属性在父对象上不可变,但让子类对其进行突变,而无需子类定义@setter
即可。
因此,只需让父类定义明确禁止对父类本身进行操作的子类友好属性获取器和设置器即可:
class Configuration:
param1 = 'a'
@property
def param2(self):
try:
return self._param2 # If child has an override, use that value
except AttributeError:
return self.param1 + 'x' # Otherwise use parent default
@param2.setter
def param2(self, new_val):
# Treat as immutable on parent
if type(self) is Configuration:
raise AttributeError("can't set attribute")
# But allow it to be set on child
self._param2 = new_val # Or type(self)._param2 = new_val if it should be set on class
@param2.deleter
def param2(self):
# Treat as immutable on parent
if type(self) is Configuration:
raise AttributeError("can't delete attribute")
del self._param2 # Or del type(self)._param2 if it's a class attribute, not instance
现在,您的孩子的实例可以变得笨拙,松散等,但是原始父类无法更改(您可以手动执行Configuration._param2 = 'new value'
,但这违反了下划线前缀名称的实现细节约定)。
请注意,如果你需要这些特性来包装类的属性,而不是实例属性(因此改变config.param2
应该改变param2
为所有其他实例Proj1
),一致性,你想要的行为时,直接分配工作Proj1.param2
为好。
这得到丑陋的,为定义property
上通过类,而不是实例直接访问类属性的工作,需要一个元类。
我不会在这里详细介绍,但是您可以在底部阅读更多内容,以 。
2楼
为了阐明我在基类中定义setter的含义,请考虑以下几点:
class Configuration:
param1 = "a"
@property
def param2(self):
return getattr(self, "_param2", self.param1 + "x")
@param2.setter
def param2(self, value):
self._param2 = value
class Proj1(Configuration):
param3 = "blah"
config = Proj1()
print(config.param2) # ax
config.param2 = "new_val"
print(config.param2) # new_val
而且,您可以对希望随心所欲覆盖的任何类属性执行相同的操作。