【发布时间】:2021-11-06 13:30:47
【问题描述】:
我需要使用/模仿语法糖语法在子类中动态装饰 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'
Attempt 1 引发错误,因为(我猜)用括号设置属性。所以在尝试2中我使用了一个辅助函数,因为lambda不允许初始化,'='语句,同样没有成功。
-
有没有办法动态模仿属性 getter/setter 装饰器? (可能没有额外的导入) 还有其他方法吗?
-
额外:为什么 super 没有属性就不能工作?
super().x(v)->TypeError: super(type, obj): obj must be an instance or subtype of type
编辑:
- Extra 的答案:来自文档:零参数形式仅适用于类定义[...]
- 使用 python3.9
【问题讨论】:
-
你用的是什么python版本?您粘贴的代码(第一个块)没有运行 paiza.io/projects/e/6mSBY6t8rUdx4fgVGih7vA?theme=twilight 或者您可能在那里粘贴了不同的块?
-
super使用静态出现super的类基于一些编译器魔法获取其默认参数。在本例中,即为Dec,但您打算使用c。尝试使用class语句而不是调用type来定义A_Dec。 -
super也实现了__getattribute__,所以它对属性查找的处理不同于“普通”类。super().x = v不触发属性设置器或错误是有意(以及为什么),我不知道。 -
@Niel Godfrey Ponciano 对不起,我忘记了 getter 的属性,
__call__中的super,现在应该可以工作了。 Python 3.9
标签: python inheritance properties overwrite