• Fluent Python: @property


    Fluent Python 9.6节讲到hashable Class,

    为了使Vector2d类可散列,有以下条件:

    (1)实现__hash__方法

    (2)实现__eq__方法

    (3)让Vector2d向量不可变

    如何让Vector2d类实例的向量只读呢?可以使用property,如下所示:

     1 class Vector2d:
     2     def __init__(self, x, y):
     3         self.__x = x
     4         self.__y = y
     5 
     6     @property  # The @property decorator marks the getter method of a property.
     7     def x(self):
     8         return self.__x
     9 
    10     @property  # The @property decorator marks the getter method of a property.
    11     def y(self):
    12         return self.__y
    13 
    14     def __hash__(self):
    15         return hash(self.__x) ^ hash(self.__y)
    16 
    17     def __eq__(self, other):
    18         return hash(self) == hash(other)
    19 
    20     def __iter__(self):
    21         return (i for i in (self.__x, self.__y))

    现在我们在控制台尝试修改x或者y:

    >>> import Example9_7
    >>> v1 = Example9_7.Vector2d(3, 4)
    >>> v1.x 
    3
    >>> v1.x = 4
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: can't set attribute
    >>> v1.y = 5
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: can't set attribute

    这是我们想要的行为,但是为什么加上@properly装饰器后就变为只读了呢?我们需要对property有更深入的了解。

    An Example To Begin With:

    在深入了解property之前,我们先来看看property的应用场景:

    假设我们写了一个关于温度的类:

    class Celsius:
        def __init__(self, temperature=0):
            self.temperature = temperature
    
        def get_fahrenheit(self):
            return self.temperature * 1.8 + 32

    并且这个类渐渐变的很流行,被很多用户所调用,有一天,一个用户跑来建议说,温度不应该低于绝对温度-273摄氏度,他要求我们实现这个限制。

    为了这样实现用户的要求,我们更新为v1.1:

    class Celsius:
        def __init__(self, temperature=0):
            self.__temperature = temperature
    
        def get_fahrenheit(self):
            return self.__temperature * 1.8 + 32
        
        def get_temperature(self):
            return self.__temperature
    
        def set_temperature(self, value):
            if value < -273:
                raise ValueError("Temperature below -273 is not possible.")
            self.__temperature = value

    用户的要求是实现了 可以这里有个问题,用户的代码里任然是这样获取温度的:

    c = Celsius(37)
    c.temperature = 20 current_temperature
    = c.temperature

    而且代码里有成百上千行如此的代码,这些代码不得不改为:

    c.set_temperature(20)
    c.get_temperature()

    对于用户来说这是很头疼的问题,因为我们的修改不是backward compatible.

    The Power of @property:

    对于这个问题,更为Pythonic的解决方式如下:

     1 class Celsius:
     2     def __init__(self, temperature=0):
     3         self.__temperature = temperature
     4 
     5     def get_fahrenheit(self):
     6         return self.__temperature * 1.8 + 32
     7 
     8     def get_temperature(self):
     9         return self.__temperature
    10 
    11     def set_temperature(self, value):
    12         if value < -273:
    13             raise ValueError("Temperature below -273 is not possible.")
    14         self.__temperature = value
    15 
    16     temprature = property(get_temperature, set_temperature)

    这样,用户任然像以前一样访问temprature:

    >>> c1 = property_demo.Celsius(10)
    >>> c1.temprature
    10
    >>> c1.temprature = 20
    >>> c1.temprature
    20

    因此我们既实现了对termperature的限制,有保证了向后兼容

    Digging Deeper into Property:

     在Python里,property()是一个内建函数(Built-in function),它返回property 对象,函数原型是:

    property(fget=None, fset=None, fdel=None, doc=None)
    # fget is function to get value of the attribute, fset is function to set value of the attribute, fdel is function to delete the attribute and doc is a string (like a comment).
    >>> property()
    <property object at 0x7f50058bc5e8>

    我们之前的示例可以分解为:

    # make empty property
    temperature = property()
    # assign fget
    temperature = temperature.getter(get_temperature)
    # assign fset
    temperature = temperature.setter(set_temperature)

    我们也可以用装饰器来实现以上的功能:

    class Celsius:
        def __init__(self, temperature=0):
            self.__temperature = temperature
    
        def get_fahrenheit(self):
            return self.__temperature * 1.8 + 32
    
        @property
        def temperature(self):
            return self.__temperature
    
        @temperature.setter
        def temperature(self, value):
            if value < -273:
                raise ValueError("Temperature below -273 is not possible")
            self.__temperature = value

    装饰器版本是更为简单,推荐的方式。

  • 相关阅读:
    java优雅的使用elasticsearch api
    springboot mybatis优雅的添加多数据源
    java通过shield链接Elasticsearch
    java spark-streaming接收TCP/Kafka数据
    hiveQL求差集
    hiveQL去重
    Ubuntu16.04+Opencv3.3的安装教程
    TensorFlow池化层-函数
    TensorFlow激活函数+归一化-函数
    TensorFlow图像预处理-函数
  • 原文地址:https://www.cnblogs.com/z-joshua/p/7596500.html
Copyright © 2020-2023  润新知