我需要使用/模仿语法糖语法在子类中动态装饰 getter 和 setter 对方法。
我正在努力解决 setter 的实现问题。
class A:
def __init__(self, x):
print('init')
self.__x = x
@property
def x(self):
print('getter')
return self.__x
@x.setter
def x(self, v):
print('setter')
self.__x = v
class Dec:
def __init__(self):
print('init - dec')
def __call__(self, cls):
c = type('A_Dec', (cls,), {})
# super-init
setattr(c, '__init__', lambda sub_self, x: super(type(sub_self), sub_self).__init__(x))
# getter
setattr(c, 'x', property(lambda sub_self: super(type(sub_self), sub_self).x))
# setter - see below
return c
dec_A = Dec()(A)
dec_a = dec_A('p')
print(dec_a.x)
输出
init - dec
init
getter
p
如果我尝试使用以下方法在 Dec
、dec_a.x = 'p'
中实现 setter 方法,我会收到以下错误:
# setter-statements of __call__
# Attempt 1
setattr(c, 'x', property(fset=lambda sub_self, v: super(type(sub_self), sub_self).x(v)))
# AttributeError: unreadable attribute
# Attempt 2 - auxiliary function
def m(sub_self, v):
print('--> ', sf, super(type(sub_self), sub_self))
super(type(sub_self), sub_self).x = v
# Attempt 2.A
setattr(c, 'x', eval('x.setter(m)'))
# NameError: name 'x' is not defined
# Attempt 2.B
setattr(c, 'x', property(fset=lambda sf, v: m(sf, v)))
# AttributeError: unreadable attribute
# Attempt 2.C: !! both at once, `fget`and `fset` so, in case, comment the getter in the above code to avoid conflicts
setattr(c, 'x', property(fget=lambda sub_self: super(type(sub_self), sub_self).x, fset=m))
# AttributeError: 'super' object has no attribute 'x'
# Attempt 2.D
p = property(fget=lambda sub_self: super(type(sub_self), sub_self).x, fset=m)
setattr(c, 'x', p)
# AttributeError: 'super' object has no attribute 'x'
尝试 1 会引发错误,因为(我猜)用括号设置属性。因此,在尝试 2 中,我使用了辅助函数,因为 lambda
不允许初始化、“=”语句,但同样没有成功。
有没有办法动态模仿属性 getter/setter 装饰器? (可能没有额外的导入) 还有其他方法吗?
额外:为什么 super 在没有属性的情况下不起作用?
super().x(v)
->TypeError: super(type, obj): obj 必须是 type 的实例或子类型
编辑:
- 额外答案:来自文档:零参数形式仅适用于类定义内部[...]
- 使用 python3.9
最佳答案
属性 setter 未正确设置。为了形象化这一点,如果没有为属性显式设置 setter,则该属性将变为只读,如 documented 。
class Parrot: def __init__(self): self._voltage = 100000 @property def voltage(self): """Get the current voltage.""" return self._voltage
The @property decorator turns the voltage() method into a “getter” for a read-only attribute with the same name
假设我们有这个:
class A:
def __init__(self, x):
self.__x = x
@property
def x(self):
return self.__x
a = A(123)
print(a.x) # will display "123"
a.x = 456 # will display "AttributeError: can't set attribute"
在原始代码中,您创建了一个新类型A_Dec
。您显式设置 getter:
# getter
setattr(c, 'x', property(lambda sub_self: super(type(sub_self), sub_self).x))
但是您没有显式设置任何 setter,从而使 x
属性变为只读。这会导致此代码出错:
dec_a.x = 'new value!' # will display "AttributeError: can't set attribute"
解决方案1
不要明确定义 getter。这样,对 x
的所有访问都将委托(delegate)给实际的类 A
。
解决方案2
如果定义了 getter,那么还要定义 setter。
...
class Dec:
...
def __call__(self, cls):
...
# setter
x_property = getattr(c, 'x')
x_setter = getattr(x_property, 'setter')
setattr(c, 'x', x_setter(lambda sub_self, v: super(type(sub_self), type(sub_self)).x.fset(sub_self, v)))
...
...
c.x.setter
的用法如 documented :A property object has
getter
,setter
, anddeleter
methods usable as decorators.fset
的用法如 documented :fset
is a function for setting an attribute value... The returned property object also has the attributesfget
,fset
, andfdel
corresponding to the constructor arguments.
因此添加以下行将会成功:
dec_a.x = 'new value!'
print(dec_a.x)
输出:
setter
getter
new value!
更多引用:
关于python - 动态添加/覆盖property属性的setter和getter,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69125694/