美文网首页工具癖每天写1000字Python
详解python当中的@property对象

详解python当中的@property对象

作者: 九日照林 | 来源:发表于2019-02-02 16:29 被阅读33次

python当中有个概念叫property,这个概念或者说模块我们经常用到,但是这个到底是什么,可能比较少谈论这个。我们不妨以一个例子开始。

设置一个温度模块

假设说现在有一家公司想要我们开发一个类,输入温度之后,记录某个地方的温度,并且可以转换成华氏温度

class Celius:
    def __init__(self, temperature):
        self.temperature = temperature

    def to_feb(self):
        return (self.temperature * 1.8) + 32
%config ZMQInteractiveShell.ast_node_interactivity='all'
a=Celius(37)
a.temperature
a.to_feb()
37
98.60000000000001

我们看到,的确输出结果,而且转换成功了。

版本2.0 希望对温度设置的时候有限制

其实我们上面的温度类已经满足要求了,但是客户希望我们能够对输入温度的值有个把控,不希望低于绝对零度-273^{\circ},因此需要对输入值进行一个判断先。

class Celius:
    def __init__(self, temperature):
        self.temperature = self.setter_temperature(temperature)

    def to_feb(self):
        return (self.temperature * 1.8) + 32

    def setter_temperature(self, val):
        if val <= -273:
            raise ValueError(
                'The temperature you input is below absolute zero')
        else:
            self.__temperature=val
%config ZMQInteractiveShell.ast_node_interactivity='all'
a=Celius(-300)

---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-11-97b5139ec1b0> in <module>()
      1 get_ipython().run_line_magic('config', "ZMQInteractiveShell.ast_node_interactivity='all'")
----> 2 a=Celius(-300)
<ipython-input-9-a8e9c60a97bc> in __init__(self, temperature)
      1 class Celius:
      2     def __init__(self, temperature):
----> 3         self.temperature = self.setter_temperature(temperature)
      4 
      5     def to_feb(self):
<ipython-input-9-a8e9c60a97bc> in setter_temperature(self, temperature)
      9         if temperature <= 273:
     10             raise ValueError(
---> 11                 'The temperature you input is below absolute zero')
     12         else:
     13             return temperature

ValueError: The temperature you input is below absolute zero

在上面我们看到对于输入值先执行了setter_temperature函数,对输入值进行了预先判断,然后再返回或者报错。

私有变量

但是实际上还是有存在重新赋值不检测的情况出现,比如我先赋值一个合法的值,但重新赋值的时候不管了。

a=Celius(300)
a.temperature
a.to_feb()

300

572.0

a.temperature=-300
a.temperature

-300

以上就没有报错了,这样是不行的。因此,我们想到,干脆直接让temperature这个属性不可见,变成一个私有变量的东东,这样就不会有人手贱去恶作剧乱修改了,而变成私有变量只需要在前面加上两根下划线__temperature。不信的话,我们试试看

class Celius:
    def __init__(self, temperature):
        self.__temperature = self.setter_temperature(temperature)

    def to_feb(self):
        return (self.__temperature * 1.8) + 32

    def setter_temperature(self, val):
        if val <= -273:
            raise ValueError(
                'The temperature you input is below absolute zero')
        else:
            self.__temperature=val

a=Celius(300)
a.temperature


---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-18-f2edb7aca8a1> in <module>()
      1 a=Celius(300)
----> 2 a.temperature

AttributeError: 'Celius' object has no attribute 'temperature'

可以看到我们的确已经没有temperature这个属性,但实际上我们可以通过_ObjectName__temperature的方式来访问。

a._Celius__temperature
300

这样无论如何,对于不会编程的人来说,想随便不经过校验是否是绝对零度就修改temperature属性的可能性就大大降低了。然后,如果我们想修改temperature的属性,就可以通过setter_temperature的函数去修改,慢着,可我们怎么返回temperature的值呢?所以我们需要新增一个函数来返回temperature这个值。于是结果如下:

class Celius:
    def __init__(self, temperature):
        self.__temperature = self.setter_temperature(temperature)

    def to_feb(self):
        return (self.__temperature * 1.8) + 32

    def getter_temperature(self):
        return self.__temperature

    def setter_temperature(self, val):
        if val <= -273:
            raise ValueError(
                'The temperature you input is below absolute zero')
        else:
            self.__temperature=val
%config ZMQInteractiveShell.ast_node_interactivity='all'
a=Celius(300)
a.getter_temperature()
300
a.setter_temperature(-300)
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-27-4631b6ae290e> in <module>()
----> 1 a.setter_temperature(-300)
<ipython-input-24-70a0b8187f0a> in setter_temperature(self, temperature)
     12         if temperature <= 273:
     13             raise ValueError(
---> 14                 'The temperature you input is below absolute zero')
     15         else:
     16             return temperature
ValueError: The temperature you input is below absolute zero

这下子我们比较安心了,因为用户没有办法通过a.temperature这个属性去修改你的温度,只能通过getter_temperature去获取温度值,通过setter_temperature去校验是否是绝对零度以下。但是这样写是不是有点冗余?因为用户还得记两个函数去分别获取或者是修改值,对于完全不懂编程的用户来说,这个用户体验分数是很低的。那么这个时候就是轮到property方法大显身手了。

在这里我们将获取温度和设置温度的getter_temperaturesetter_temperature两个函数作为参数,传入到property这个类当中,返回一个对象,这个对象是既能够通过a.temperature的方式返回温度值,也能通过a.temperature=-250的方式先检验值再设置值。这就实现了简便校验的双重便利。

class Celius:
    def __init__(self, temperature):
        self.__temperature = temperature

    def to_feb(self):
        return (self.__temperature * 1.8) + 32

    def getter_temperature(self):
        return self.__temperature

    def setter_temperature(self, val):
        if val <= -273:
            raise ValueError(
                'The temperature you input is below absolute zero')
        else:
            self.__temperature=val

    temperature = property(getter_temperature, setter_temperature)

%config ZMQInteractiveShell.ast_node_interactivity='all'
a=Celius(300)
a.temperature

300

a.temperature=-300

---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-30-c50d65fc7ecf> in <module>()
----> 1 a.temperature=-300

<ipython-input-28-f6cfb4a4923a> in setter_temperature(self, temperature)
     12         if temperature <= 273:
     13             raise ValueError(
---> 14                 'The temperature you input is below absolute zero')
     15         else:
     16             return temperature

ValueError: The temperature you input is below absolute zero

在上面我们可以看到,a.temperaturetemperature其实是来自于类当中property返回的对象,而不是其他的temperatureproperty对象有三个方法可以使用,分别是gettersetterdelete,这三个方法分别是在返回值设置值删除值的时候进行一些操作。

在这里就牵扯到了数据封装的概念,其实数据封装概念很好理解,其实就是数据和对数据的操作其实是黑匣子,你用a.temperatue=300这样的方式去赋值的时候并没有想到后面还有这么多函数,你还以为是简简单单地这么搞出来。所以的话,数据封装的概念就是,将数据和对数据的操作绑定起来的这个机制。这些操作就是上面说到的那三种,分别是gettersetterdelete

我们也可以用另外一种方式来传入参数。

temperature=property()
temperature=temperature.getter(getter_temperature())
temperature=temperature.setter(setter_temperature())

@property语法糖

上面的类定义当中的最后一步temperature = property(getter_temperature, setter_temperature)其实可以用语法糖来生成。可以改成:

class Celius:
    def __init__(self, temperature):
        self.__temperature = temperature

    def to_feb(self):
        return (self.__temperature * 1.8) + 32
    @property
    def temperature(self):
        return self.__temperature
    @temperature.setter
    def temperature(self, val):
        if val <= -273:
            raise ValueError(
                'The temperature you input is below absolute zero')
        else:
            self.__temperature=val

    

%config ZMQInteractiveShell.ast_node_interactivity='all'
a=Celius(300)
a.temperature

300

a.temperature=-300

---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-60-c50d65fc7ecf> in <module>()
----> 1 a.temperature=-300

<ipython-input-58-9812e4d2cbb9> in temperature(self, val)
     12         if val <= -273:
     13             raise ValueError(
---> 14                 'The temperature you input is below absolute zero')
     15         else:
     16             self.__temperature=val

ValueError: The temperature you input is below absolute zero

a.temperature=200
a.temperature
200

@property将下面的函数变成了一个property对象,跟之前的写法的作用是一样的

temperature=property()
temperature=temperature.getter(temperature)

这里的第一行temperatureproperty对象,第二行中括号里面的temperature是返回temperature的那个函数,它作为参数传入property对象中。

以上就是property的来龙去脉,简单说,就是property具有返回和修改值的方法,能够使得修改值和返回值写起来更加便利。

相关文章

  • 详解python当中的@property对象

    python当中有个概念叫property,这个概念或者说模块我们经常用到,但是这个到底是什么,可能比较少谈论这个...

  • python @property

    参考 Python进阶之“属性(property)”详解 - Python - 伯乐在线

  • Python @property 详解

    类方法转为只读属性 重新实现属性的setter, getter, deleter方法 类方法转为只读属性 pro...

  • Python @property 详解

    一、概述 python中 @property 是python的一种装饰器,是用来修饰方法的。我们可以使用@pro...

  • JavaScript对象的property属性详解

    JavaScript中对象的property有三个属性:1.writable。该property是否可写。2.en...

  • Python第三周

    面向对象的进阶 包装器:@property(getter)、@setter 之前我们讨论过Python中属性和方法...

  • python 类

    类 用来描述具有相同的属性和方法的对象的集合。python中的类就是对象。 python 中的元类详解 创建Dog...

  • python --@property装饰器详解

    既要保护类的封装特性,又要让开发者可以使用“对象.属性”的方式操作操作类属性,除了使用 property() 函数...

  • Python中的property属性

    Python中有个很赞的概念,叫做property,它使得面向对象的编程更加简单。在详细解释和深入了解Python...

  • python中的type和object详解

    python中的type和object详解 关于这篇博客 这篇博客主要描述Python的新风格对象(new-sty...

网友评论

    本文标题:详解python当中的@property对象

    本文链接:https://www.haomeiwen.com/subject/mgwssqtx.html