• 【Python】解决类中特性(property)覆盖同名属性(attribute)报错问题


    一、问题描述

    在设计一类加法的数据结构中,定义 Foo 对象可以拥有自己的取值 value 以及一系列可以通过累加得到该 Foo 对象的取值 value 的加法因子 factors

    为简化对 Foo 对象的维护,希望每次获取 Foo 的取值 value 时,可以直接通过其加法因子列表元素的累加来获取其 value,而不需要每次手动对其 value 进行更新,因此考虑通过装饰器 @property 来实现该功能,如以下代码所示。

    class Foo:
        def __init__(self, value, factors=None):
            self.value = value
            self.factors = [self] if factors is None else factors
    
        @property
        def value(self):
            return sum([factor.value for factor in self.factors]) if len(self.factors) > 1 else self.value
    
    
    foo1 = Foo(value=3)
    foo2 = Foo(value=5)
    foo = Foo(value=8, factors=[foo1, foo2])
    print(f"The value of foo1 is: {foo1.value}.")
    print(f"The value of foo2 is: {foo2.value}.")
    print(f"The value of foo  is: {foo.value}.")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    然而,运行该段代码后,程序报错 AttributeError,详细报错信息如下:

    Traceback (most recent call last):
      File "attribute_property.py", line 11, in <module>
        foo1 = Foo(value=3)
      File "attribute_property.py", line 3, in __init__
        self.value = value
    AttributeError: can't set attribute
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    二、问题分析

    出现该报错的原因是因为 property 装饰器作为特性重写了 value 属性,因此当创建新的 Foo 对象时,line 3 实际上将找到 @property 重写的 value 方法,而由于 @property 实际上只实现了 @getter 功能,因此可认为它只实现了可读属性的功能,而并没有实现 @setter 的功能。简单来说,这个时候是不能为它赋值的。(关于 @getter@setter 等相关装饰器内容请参见相关博客。)

    三、解决方案

    从上例中可以看到,此时我们是无法使用特性(property)直接覆盖同名属性(attribute)的。

    一个简单的解决方案是将原来的 value 属性设置为保护属性,即在其变量名前添加下划线成为 _value。对应地,将 @property 实现的类方法中所返回的 value 属性更改为 _value。既然此时这二者都不同名了,自然也不会出现设置属性报错的问题了。

    修改后的代码及运行结果如下所示:

    class Foo:
        def __init__(self, value, factors=None):
            self._value = value
            self.factors = [self] if factors is None else factors
    
        @property
        def value(self):
            return sum([factor.value for factor in self.factors]) if len(self.factors) > 1 else self._value
    
    
    foo1 = Foo(value=3)
    foo2 = Foo(value=5)
    foo = Foo(value=8, factors=[foo1, foo2])
    print(f"The value of foo1 is: {foo1.value}.")
    print(f"The value of foo2 is: {foo2.value}.")
    print(f"The value of foo  is: {foo.value}.")
    ---------------------------------------------
    The value of foo1 is: 3.
    The value of foo2 is: 5.
    The value of foo  is: 8.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    此外,如果我们想要直接从外部重新对 _value 进行赋值,也可以通过添加 @setter 方法来实现。

    class Foo:
        def __init__(self, value, factors=None):
            self._value = value
            self.factors = [self] if factors is None else factors
    
        @property
        def value(self):
            return sum([factor.value for factor in self.factors]) if len(self.factors) > 1 else self._value
    
        @value.setter
        def value(self, value):
            self._value = value
    
    
    foo1 = Foo(value=3)
    foo2 = Foo(value=5)
    foo = Foo(value=8, factors=[foo1, foo2])
    print(f"The value of foo1 is: {foo1.value}.")
    print(f"The value of foo2 is: {foo2.value}.")
    print(f"The value of foo  is: {foo.value}.")
    
    foo1.value = 4
    foo2.value = 6
    print()
    print(f"The value of foo1 is: {foo1.value}.")
    print(f"The value of foo2 is: {foo2.value}.")
    print(f"The value of foo  is: {foo.value}.")
    ---------------------------------------------
    The value of foo1 is: 3.
    The value of foo2 is: 5.
    The value of foo  is: 8.
    
    The value of foo1 is: 4.
    The value of foo2 is: 6.
    The value of foo  is: 10.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    上例中,当我们分别对 foo1foo2value 进行赋值,该过程将调用 Foo@setter 方法,对保护属性 _value 进行重新赋值。而当计算 foovalue 时,由于其加法因子 foo1foo2value 均改变,其 value 的取值随之发生动态变化。

  • 相关阅读:
    Ubuntu系统下创建虚拟AP和STA扫描/关联/认证/ping
    java计算机毕业设计游泳馆信息管理系统源码+数据库+系统+部署+lw文档
    folium 增加搜索、经纬度弹出,字段弹出的方法
    三层架构与web结合图解
    cocos2d-lua:骨骼动画的使用
    SpringBoot(基础篇 ==> 框架介绍、创建方式
    python 获取apk信息
    司空见惯 - 英雄扫雷鼠
    [manjaro]更新后内核文件加载失败
    SpringBoot+ECharts+Html 字符云/词云案例详解
  • 原文地址:https://blog.csdn.net/qq_42886635/article/details/128168237