python - 动态添加/覆盖property属性的setter和getter

标签 python inheritance properties overwrite

我需要使用/模仿语法糖语法在子类中动态装饰 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

如果我尝试使用以下方法在 Decdec_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, and deleter methods usable as decorators

  • .fset 的用法如 documented :

    fset is a function for setting an attribute value... The returned property object also has the attributes fget, fset, and fdel 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/

相关文章:

python - select.select 套接字和管道问题

python - 我什么时候应该重新定义数字?

python - pyodbc sqlserver游标小数四舍五入

java - 如何使用 hibernate.properties 文件而不是 hibernate.cfg.xml

python - Paramiko 。按修改时间获取文件

java - 自定义注解参数化继承其他注解

c# - 在 C# 中向现有代码添加自定义功能的最佳方式是什么?

cocoa - 将自定义方法添加到子类 NSManagedObject

c# - 将自动属性转换为通知属性(WPF 中的 MVVM)

msbuild - 我在哪里可以获得项目文件中所有可用属性的列表?