Python 魔法方法2https://blog.csdn.net/qq_49873907/article/details/130320814?spm=1001.2014.3001.5501
本篇文章主要介绍与属性访问相关的函数和魔法方法,主要包括__getattr__()、__getattribute__()、__setattr__()、__delattr__()。
一、与属性访问相关的函数
对于与属性访问相关的函数分别为hasattr()、getattr()、setattr()、delattr()。我们通过一个例子来进行简单说明:
class C:
def __init__(self,name,age):
self.name = name
self.__age = age #定义一个私有的属性age [属性名前加两个下划线__]
c = C("Python",22)
hasattr(c,"name") #这里hasattr第二个参数要加上引号
# True
hasattr(c,"age") #要注意访问私有变量要用"_类名__属性名"的形式进行强访问
# False
hasattr(c,"_C__age")
# True
getattr(c,"name") #获取属性值
# 'Python'
getattr(c,"_C__age")
# 22
setattr(c,"_C__age",18) #设置属性值
getattr(c,"_C__age")
# 18
delattr(c,"_C__age") #删除属性值
hasattr(c,"_C__age")
# False
通过上述代码不难看出,这些和属性访问相关的方法和点好运算符是等价的。
二、与属性访问相关的魔法方法
2.1 __getattribute__()
对于__getattrible__()魔法方法,与之对应的是getattr()函数,这里有点特殊,因为可能大家都会比较认同getattr()函数对应的魔法方法应该是__getattr__()才更加匹配,可事实不是这样,我们通过一个例子来简单说明:
class C:
def __init__(self,name,age):
self.name = name
self.__age = age
def __getattribute__(self,attrname):
print("访问了getattribute")
return super().__getattribute__(attrname)
'''
super()调用的是父类的object的__getattribute__()方法
Python中所有类默认继承object类,而调用object中的
__getattributr__(attrname)的结果就是return attrname
'''
def __getattr__(self,attrname):
if attrname == "Python": # 当访问的属性名是Python时,打印结果
print("I Love Python")
else:
raise AttributrError(attrname)
c = C("hello",22)
getattr(c,"name")
# 访问了getattribute
# 'hello'
c.name #点号运算符和getattr()等价
# 访问了getattribute
# 'hello'
getattr(c,"Python")
# 访问了getattribute
# I Love Python
getattr(c,"123")
# 访问了getattribute
'''
Traceback (most recent call last):
File "<pyshell#28>", line 1, in <module>
getattr(c,"123")
File "<pyshell#23>", line 12, in __getattr__
raise AttributrError(attrname)
NameError: name 'AttributrError' is not defined
'''
通过上述代码,我们可以看出,只有当用户试图访问不存在的属性时,才会去触发__getattr__()方法,不过还是会先访问__getattribute__()方法。
2.2 __setattr__()
setattr() 对应的魔法方法是__setattr__() ,对属性进行赋值操作,虽然不像__getattribute__()那样,不过其中还是有需要注意的点的。
class C:
def __setattr__(self,name,value):
self.name = value #陷入无限递归
c = C()
setattr(c,"name","python")
#死循环报错
'''
Traceback (most recent call last):
File "<pyshell#35>", line 1, in <module>
setattr(c,"name","python")
File "<pyshell#33>", line 3, in __setattr__
self.name = value
File "<pyshell#33>", line 3, in __setattr__
self.name = value
File "<pyshell#33>", line 3, in __setattr__
self.name = value
[Previous line repeated 990 more times]
RecursionError: maximum recursion depth exceeded
'''
class C:
def __setattr__(self,name,value):
return super().__setattr__(name,value) #调用父类解决
c =C()
setattr(c,"name","Python")
getattr(c,"name")
# 'Python'
class C:
def __setattr__(self,name,value):
self.__dict__[name] = value #使用__dict__利用集合实现
c = C()
setattr(c,"name","Python")
getattr(c,"name")
# 'Python'
通过上述代码可以看出,进行赋值操作的时候陷入了无限循环,因为__setattr__()本身就是进行赋值,执行方法体中的代码再一次进行了赋值,这不就相当于实现递归,自己调用自己了吗。我们有两种解决方式,第一种是直接交给父类来处理,实现函数原本的功能,在一个是借助__dict__()函数,利用集合来实现赋值的操作。
2.3 __delattr__()
__delattr__()魔法方法对应着delattr()函数,用来删除属性值。它和__setattr__()同样要注意递归的问题,这里可以参考上面__setattr__()的说明。
# 法1
class C:
def __setattr__(self,name,value):
return super().__setattr__(name,value)
def __delattr__(self,name):
return super().__delattr__(name)
c = C()
setattr(c,"name","haha")
c.__dict__
# {'name': 'haha'}
delattr(c,"name")
c.__dict__
# {}
# 法2
class C:
def __setattr__(self,name,value):
self.__dict__[name] = value
def __delattr__(self,name):
del self.__dict__[name]
c = C()
setattr(c,"name","haha")
c.__dict__
# {'name': 'haha'}
delattr(c,"name")
c.__dict__
# {}