QQ登录

只需一步,快速开始

开启左侧

Python学习第六十二天—类和对象11-属性访问相关的魔法方法

[复制链接]
15271953841 发表于 2024-3-26 09:10:16 | 显示全部楼层 |阅读模式

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

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

x
本帖最后由 15271953841 于 2024-3-26 09:11 编辑

属性访问相关的魔法方法
跟属性访问相关的函数和魔法方法,我们知道对象可以通过“.”号进行属性访问,通过“.”号我们不仅可以访问一个应有的属性,还可以创建一个未曾有过的属性,在Python中有几个BIF函数是专门为对象的属性访问服务的,它们是hasattr(),getattr(),setattr(),delattr()。
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,name,age):
        self.name = name
        self.__age = age           (__age是私有变量,前面是两个下划线)

        
>>>c = C("小甲鱼",18)
>>>c.name
'小甲鱼'
>>>hasattr(c,"name")                (hasattr检测c对象是不是有“name”这个属性,注意:”name”这个属性名要以字符串形式传递进去,要加引号)
True
>>>getattr(c,"name")                 (getattr是获取对象中的某个属性值)
'小甲鱼'
>>>getattr(c,"_C__age")                (如果我们想要获取一个私有变量的值,要知道第58课时学过的name mangling的机制,就是名字改编术,_C__x)
18
>>>setattr(c,"_C__age",19)             (setattr()是设置对象中指定属性的值,设置时,属性值直接跟在属性名的后面,用逗号隔开)            
>>>getattr(c,"_C__age")                (不难发现,setattr与getattr和“.”运算符的作用效果时等价的
19
>>>delattr(c,"_C__age")               (delattr就是相当于del语句)
>>>hasattr(c,"C__age")                 (用hasattr判断它是否存在)
False
这几个函数hasattr(),getattr(),setattr(),delattr()也有对应的魔法方法,与之相呼应。
(一)__getattr__(),可能很多人会猜测到这个魔法与getattr()函数相呼应,那么你错了。事实上是,__getattribute__()魔法方法与getattr()函数相呼应,这个别搞错了。
>>>class C:
    def __init__(self,name,age):
        self.name = name
        self.__age = age
    def __getattribute__(self,attrname):
        print("拿来吧你~")
        return super().__getattribute__(attrname)

   
>>>c = C("小姐姐",18)
>>>c.name                  (通过“.”号访问,也是被拦截到了)
拿来吧你~
'小姐姐'
>>>getattr(c,"name")          (被拦截了,先打印“拿来吧你~”,吧小姐姐给拿过去了)
拿来吧你~
'小姐姐'
>>>c._C__age                  (通过“.”号访问,也是被拦截到了)
拿来吧你~
18
>>>c.FishC                   (去访问一个不存在的属性,也会被拦截,先打印“拿来吧你~”,然后再抛出红色的一串异常)
拿来吧你~
Traceback (most recent call last):
  File "<pyshell#27>", line 1, in <module>
    c.FishC
  File "<pyshell#18>", line 7, in __getattribute__
    return super().__getattribute__(attrname)
拿来吧你~
拿来吧你~
AttributeError: 'C' object has no attribute 'FishC'
那么这个__getattr__魔法方法是干吗的呢?虽然它与getattr()函数长的很像,也是跟访问有关系,不过它是只有在用户试图去获取一个不存在的属性的时候,才会被触发的魔法方法,但是如果有__getattribute__()魔法方法存在,还是要先调用__getattribute__魔法方法,然后再调用__getattr__()的魔法方法。
>>>class C:
    def __init__(self,name,age):
        self.name = name
        self.__age = age
    def __getattr__(self,attrname):
        if attrname == "FishC":
            print("I love little sister.")
        else:
            raise AttributeError(attrname)
    def __getattribute__(self,attrname):
        print("拿来吧你~")
        return super().__getattribute__(attrname)
(这里用“if attrname == “FishC””的”FishC”设计了一个机关,尽管对象不存在一个叫”FishC”的属性名,但是如果你去访问“FishC”这个属性名的话,我们也会给予回复的,打印一个“I love little sister.“,让后其它情况就会抛出AttributeError的异常。
   
>>>c = C("小姐姐",18)
>>>c.FishC
拿来吧你~
I love little sister.
>>>c.x
拿来吧你~
Traceback (most recent call last):
  File "<pyshell#47>", line 1, in <module>
    c.x
  File "<pyshell#44>", line 9, in __getattr__
    raise AttributeError(attrname)
拿来吧你~
拿来吧你~
AttributeError: x
从中发现:如果我们去访问一个不存在的属性,__getattribute__()魔法方法先响应,打印了一个“拿来吧你~”,然后才会给到__getattr__的魔法方法。

(二)赋值属性对应的是__setattr__()魔法方法,但是也不能掉以轻心,因为这里面也是有坑的。
>>>class D:
    def __setattr__(self,name,value):
        self.name = value

        
>>>d=D()
>>>d.name = "小姐姐"
Traceback (most recent call last):
  File "<pyshell#53>", line 1, in <module>
    d.name = "小姐姐"
  File "<pyshell#51>", line 3, in __setattr__
    self.name = value
  File "<pyshell#51>", line 3, in __setattr__
    self.name = value
  File "<pyshell#51>", line 3, in __setattr__
    self.name = value
  [Previous line repeated 744 more times]
RecursionError: maximum recursion depth exceeded     (递归错误)
当捕捉到对象赋值操作的时候,那Python就会去调用__setattr__的魔法方法,就会执行self.name=value,将value这个参数赋值到self.name中。
为什么报错:self是对象本身,捕获到赋值操作的时候,我们去执行这个self.name=value,那不就相当于又给自己调用了一次赋值操作,然后再次执行了self.name=value,这叫无限递归,也就是死循环。
那怎么办?我们自己搞不定,可以交给super(),它老爹肯定知道怎么做,才不会出错。
>>>class D:
    def __setattr__(self,name,value):
        return super().__setattr__(name,value)
        self.name = value

        
>>>d = D()
>>>d.name = "小姐姐"
>>>d.name
'小姐姐'
>>>d.__dict__
{'name': '小姐姐'}
咱们非要自己来实现的话,可能就要动点脑筋。
我们知道,这个对象的属性和它的值,它是用一个字典的形式来保存的,就是__dict__(看第58课)
>>>class D:
    def __setattr__(self,name,value):
        self.__dict__[name] = value

        
>>>d=D()
>>>d.name = "小美女"
>>>d.name
'小美女'
>>>d.__dict__
{'name': '小美女'}

(三)我们使用__delattr__()也要注意这个死亡螺旋,利用这个字典实现属性删除操作。
>>>class D:
    def __setattr__(self,name,value):
        self.__dict__[name] = value
    def __delattr__(self,name):
        del self.__dict__[name]

        
>>>d = D()
>>>d.name = "小帅哥"
>>>d.name
'小帅哥'
>>>d.__dict__
{'name': '小帅哥'}
>>>del d.name
>>>d.__dict__
{}

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

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

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

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