QQ登录

只需一步,快速开始

开启左侧

Python学习第六十六天—类和对象15-property()

[复制链接]
15271953841 发表于 2024-4-4 11:22:11 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?注册

x
Property()函数用于返回一个property属性对象。有啥用?看下面的例子。
Class property(fget=None,fset=None,fdel=None,doc=None),虽然我们习惯叫它“函数”,但其实跟int()、str()、float()函数一样,是一个“Built-in class”(内置类)
Python 3.12.1 (tags/v3.12.1:2305ca5, Dec  7 2023, 22:03:25) [MSC v.1937 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license()" for more information.
>>>class C:
    def __init__(self):
        self._x = 250           (_x开头,有意隐藏,内部使用)
    def getx(self):
        return self._x                 (获取x的值)
    def setx(self,value):
        self._x = value                  (设置它的值)
    def delx(self):
        del self._x                     (删除)
    x = property(getx,setx,delx)            (将三个函数作为参数传递给property()函数,并且拿到它的返回值,给到x里面去)

   
>>>c = C()            (现在这个没有带下划线杠的x,就全权代理了_x,我们通过对这个x的访问或修改,都会影响到这个_x的值。)
>>>c.x
250
>>>c.x = 520
>>>c.__dict__
{'_x': 520}
>>>del c.x
>>>c.__dict__
{}
那么这个property()函数是干什么用的?
通过getattr、setattr、delattr,三个魔法方法不也可以实现相同的效果吗?这难道是重复造轮子吗?公布答案如下:
>>>class D:
    def __init__(self):                   (构造函数与上面一样)
        self._x = 250
    def __getattr__(self,name):
        if name == 'x':                 (如果name=x,就用这个x代理下横线_x)
            return self._x
        else:
            super().__getattr__(name)
    def __setattr__(self,name,value):
        if name == 'x':                    (如果name=x,要设置它,但不能直接设置成self._x=value,会出现递归问题)
            super().__setattr__('_x',value)        (在讲setattr的时候(参考第62课时),说这里面有坑,有坑就甩锅给父类,让它的父类来做)
        else:
            super().__setattr__(name,value)
    def __delattr__(self,name):
        if name == 'x':
            super().__delattr__('_x')         (删除的是_x)
        else:
            super().__delattr__(name)        (参考第62课时)

            
>>>d = D()
>>>d.x
250
>>>d.__dict__
{'_x': 250}
>>>d.x = 520
>>>d.x
520
>>>d.__dict__
{'_x': 520}
>>>del d.x
>>>d.__dict__
{}
这里可以做到同样的实现,但是使用这个魔法方法三件套是如此复杂,使用property()函数,第一个优点就是简化内饰,还有第二个函数,让菜鸟落泪,大神陶醉。大家看到,这个property()函数的前三个参数都是函数来的,传入一个函数作为它的参数,那么这个特征有没有唤起我们些许的回忆——装饰器,装饰器实现的原理:就是通过传入函数参数来实现的,这是property最经典的应用,举例:如果我们让property()函数作为装饰器来使用,会让创建只读属性的工作变得极为简单,演示一下:
>>>class E:
    def __init__(self):
        self._x = 250
    @property
    def x(self):
        return self._x

   
>>>e = E()
>>>e.x
250
>>>e.__dict__
{'_x': 250}
>>>e.x = 520
Traceback (most recent call last):
  File "<pyshell#15>", line 1, in <module>
    e.x = 520
AttributeError: property 'x' of 'E' object has no setter
>>>del e.x
Traceback (most recent call last):
  File "<pyshell#16>", line 1, in <module>
    del e.x
AttributeError: property 'x' of 'E' object has no deleter
@property装饰器理解:
让property函数作为装饰器,让创建只读属性的工作变得极为简单,所以这个e.x是只读的。
那么这是一个什么神仙原理呢?
大家知道,装饰器就是一个语法糖,将它转换成容易理解的样子:
>>>class E:
    def __init__(self):
        self._x = 250
    #@property                (用#号去掉这一行的装饰器)
    def x(self):
        return self._x
    x = property(x)               (改成这个样子)

   
>>>e = E()
>>>e.x
250
>>>e.x = 520
Traceback (most recent call last):
  File "<pyshell#47>", line 1, in <module>
    e.x = 520
AttributeError: property 'x' of 'E' object has no setter
>>>del e.x
Traceback (most recent call last):
  File "<pyshell#48>", line 1, in <module>
    del e.x
AttributeError: property 'x' of 'E' object has no deleter
因为我们这里只赋值了property的第一个参数,也就是用于实现获取的fget参数,另外两参数(参看本课时的开头)fset、fdel都采用默认值None,表示不支持写入和删除,所以才有这样的实现结果,才会出现所谓的“只读属性”。

我们用@property装饰器只是实现了第一个参数传入,那如果还有两个参数要传这么办?property的属性对象其实提供了getter、setter、deleter三个方法,这些方法其实对应了property函数三个参数接口,代码可以这么写:
>>>class E:
    def __init__(self):
        self._x = 250
    @property
    def x(self):
        return self._x
    @x.setter             ( 注意:property属性对象拥有getter、setter、deleter三个方法,这个属性对象是由property函数返回的这个东西(@x.setter、@x.deleter),所以我们看回上面的x=property(x),参数x它返回的值给到x的,所以是x.setter,这里def x(self,value)名字x也要写一样。)
    def x(self,value):
            self._x = value
    @x.deleter
    def x(self):
        del self._x

>>>e = E()
>>>e.x
250
>>>e.__dict__
{'_x': 250}
>>>e.x = 520
>>>e.x
520
>>>e.__dict__
{'_x': 520}
>>>del e.x
>>>e.__dict__
{}
不难发现,这个做法,与课程刚开始的第一个例子的效果实现效果是一模一样的,只不过使用装饰器,代码比较骚,仅此而已。
@property返回的不是对象,而是一个属性,相当于给到x一个property属性,这样对象e呼叫x的时候就会去找到property从而执行property(x).
实际开发中总会要隐藏一些对象,所以这是对隐藏对象的调用。
就是展示了property()函数的优点。

客服热线
400-1234-888 周一至周日:09:00 - 21:00
公司地址:襄阳市樊城区长虹路现代城5号楼188

创客帮MAKER.BAND青少年创客创意社区是一个融教育、科技、体育资讯为一体的综合服务平台,专注于教育创新、专注于科技体育、专注于教育资讯。

Powered by Discuz! X3.4 © 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表