Python @property 用法

本文整理自《Effective Python 编写高质量 Python 代码的 59 个有效方法》第 29 条:用纯属性取代 get 和 set 方法。

Python 类 public 属性

首先,python 类中的 public 属性可以实现 setter 和 getter 等工具方法功能,动态修改、获取类中的属性:

class Resistor(object):
    def __init__(self, ohms):
        self.ohms = ohms
        self.voltage = 0
        self.current = 0


if __name__ == '__main__':
    r1 = Resistor(50e3)
    r1.ohms = 10e3
    r1.ohms += 5e3

    print(r1.ohms)

修改属性时实现特殊行为

如果想在设置属性的时候实现特殊行为(比如顺带着将相关属性一并更新修改),那么可以改用 @property 修饰器和 setter 方法来做,比如下方继承 Resistor 的子类,在给 voltage 赋值时一并将电流值修改赋值:

class Resistor(object):
    def __init__(self, ohms):
        self.ohms = ohms
        self.voltage = 0
        self.current = 0


class VoltageResistance(Resistor):
    def __init__(self, ohms):
        super().__init__(ohms)
        self._voltage = 0

    @property
    def voltage(self):
        return self._voltage

    @voltage.setter
    def voltage(self, voltage):
        self._voltage = voltage
        self.current = self._voltage / self.ohms


if __name__ == '__main__':
    r2 = VoltageResistance(1e3)
    print("Before: %5r amps" % r2.current)
    r2.voltage = 10
    print("After: %5r amps" % r2.current)

修改属性时做验证限制

为属性指定 setter 方法时,也可以在方法里面做类型验证及数值验证,下方定义了个保证传入电阻始终大于 0 欧姆的类:

class Resistor(object):
    def __init__(self, ohms):
        self.ohms = ohms
        self.voltage = 0
        self.current = 0


class VoltageResistance(Resistor):
    def __init__(self, ohms):
        super().__init__(ohms)
        self._voltage = 0

    @property
    def voltage(self):
        return self._voltage

    @voltage.setter
    def voltage(self, voltage):
        self._voltage = voltage
        self.current = self._voltage / self.ohms


class BoundedResistance(Resistor):
    def __init__(self, ohms):
        super().__init__(ohms)

    @property
    def ohms(self):
        return self._ohms

    @ohms.setter
    def ohms(self, ohms):
        if ohms <= 0:
            raise ValueError("%f ohms must be > 0" % ohms)
        self._ohms = ohms


if __name__ == '__main__':
    r3 = BoundedResistance(1e3)
    print(r3.ohms)
    r3.ohms = 0

运行结果:

ValueError: 0.000000 ohms must be > 0
1000.0

防止父类属性被修改

class Resistor(object):
    def __init__(self, ohms):
        self.ohms = ohms
        self.voltage = 0
        self.current = 0


class FixedResistance(Resistor):
    def __init__(self, ohms):
        super().__init__(ohms)

    @property
    def ohms(self):
        return self._ohms

    @ohms.setter
    def ohms(self,ohms):
        if hasattr(self,"_ohms"):
            raise AttributeError("Can't set attribute")
        self._ohms = ohms
        
        
if __name__ == '__main__':
    r4 = FixedResistance(1e3)
    print(r4.ohms)
    r4.ohms = 2e3

注意

用 @property 方法实现 setter 和 getter 用法时,不要把程序写得太过奇怪,例如不要再某属性的 getter 方法中去修改其他属性的值,最恰当的方法是只在 @property.setter 里面修改相关的对象状态,并且要防止该对象产生预料之外的副作用。我们所编写的类是要快速修改、返回对象属性,至于比较复杂或速度慢的操作,应该放到普通方法里。

要点

  1. 编写新类,用简单的 public 属性定义接口,不要手工实现 set 和 get
  2. @property 在需要访问对象进行特殊行为时使用,应遵循最小惊讶原则,不应产生奇怪的副作用
  3. @property 方法需要迅速执行,缓慢复杂的工作不要放里面、应该用普通方法来完成
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值