开发者

一文解密Python中_getattr_和_getattribute_的用法与区别

开发者 https://www.devze.com 2023-01-14 09:18 出处:网络 作者: 古明地觉
目录__getattr____getattribute____getattr__ 当访问实例对象的某个不存在的属性时,毫无疑问会报错,会抛出 AttributeError。
目录
  • __getattr__
  • __getattribute__

__getattr__

当访问实例对象的某个不存在的属性时,毫无疑问会报错,会抛出 AttributeError。

classA:
pass

a=A()
a.xxx
"""
AttributeError:'A'objecthasnoattribute'xxx'
"""

但如果我们希望在找不到某个属性时,不要报错,而是返回默认值,该怎么做呢?这个时候我们就需要定义 __getattr__ 方法了,当实例对象找不到某个属性时会执行此方法。

classGirl:

def__init__(self):
se开发者_开发入门lf.name="古明地觉"
self.age=17

defget_info(self):
returnf"name:{self.name},age:{self.age}"

def__getattr__(self,item):
returnf"你访问了{item}属性"


girl=Girl()
print(girl.name,girl.age)#古明地觉17
print(girl.get_info)#<boundmethodGirl.get_info...>
print(girl.get_info())#name:古明地觉,age:17

print(girl.xxx)#你访问了xxx属性
print(girl.yyy)#你访问了yyy属性
print(girl.zzz)#你访问了zzz属性

所以非常简单,就是当实例对象访问了一个不存在的属性时,会执行 __getattr__ 方法。当然,如果属性存在的话,就不会执行了,而是返回相应的值。

此外 __getattr__ 还有一个用法,就php是在模块导入的时候。假设我们有一个 tools.py,里面代码如下:

def__getattr__(name):
returnf"{__name__}中不存在{name}"

name="古明地觉"
age=17

相信你明白它是干什么的了,我们来导入它:

fromtoolsimportname,age,xxx,yyy

print(name,age)#古明地觉17
print(xxx)#tools中不存在xxx
print(yyy)#tools中不存在yyy

importtools
print(tools.zzz)#tools中不存在zzz

在获取 tools.py 里面的属性时,如果不存在,那么同样会去执行 __getattr__,应该还是很简单的。

__getattribute__

__getattribute__ 被称为属性拦截器,它比 __getattr__ 要霸道的多,这两者的区别如下:

  • __getattr__:当访问的属性不存在时,才会执行此方法;
  • __getattribute__:不管访问的属性是否存在,一律执行此方法;

我们举个例子:

classGirl:

def__init__(self):
self.name="古明地觉"
self.age=17

def__getattribute__(self,item):
returnf"获取属性:{item}"


girl=Girl()
print(girl.name)#获取属性:name
print(girl.age)#获取属性:age
print(girl.xxx)#获取属性:xxx

#即便你想通过属性字典获取也是没有用的
#因为不管什么属性,都会执行__getattribute__
print(girl.__dict__)#获取属性:__dict__

并且在使用这个方法的时候,一定要谨慎,因为你一不小心就会陷入无限递归。

classGirl:

def__init__(self):
self.name="古明地觉"
self.age=17

def__getattribute__(self,item):
returngetattr(self,item)


girl=Girl()
print(girl.name)
#显然上面的代码会陷入无限递归
#因为girl.name会调用__getattribute__
#而在里面又执行了getattr(self,item),还是在获取属性
#所以又会调用__getattribute__,于是会无限递归

#可能有人说,那我换一种方式
#我将getattr(self,item)改成self.__dict__[item]可以吗
#答案也是不行的,因为self.__dictjavascript__仍是在获取属性
#只要获取属性,就会触发__getattribute__,依旧会陷入无限递归

所以 __getattribute__ 非常霸道,那么我们如何使用它呢?答案是通过父类。

classGirl:

def__init__(self):
selfphp.name="古明地觉"
self.age=17

def__getattribute__(self,item):
returnsuper().__getattribute__(item)


girl=Girl()
print(girl.name)
print(girl.age)
try:
girl.xxx
exceptAttributeError:
print("属性xxx不存在")
"""
古明地觉
17
属性xxx不存在
"""

当我们调用父类的 __getattribute__ 时,如果属性存在,它会直接返回;如果实例没有该属性,那么会检测我们是否定义了 __getattr__,定义了则执行,没定义则抛出 AttributeError。我们将这两个方法结合起来,看一个例子:

classGirl:

def__init__(self):
self.name="古明地觉"
self.age=17

def__getattr__(self,item):
print(f"__getattr__{item}")
returnf"获取属性{item}"

def__getattribute__(self,item):
print(f"__getattribute__{item}")
returnsuper().__getattribute__(item)


girl=Girl()
#不管属性是否存在,一律调用__getattribute__
#然后在里面我们又调用了父类的__getattribute__
#那么会检测属性是否存在,存在则直接获取对应的值,然后返回
print(girl.name)
"""
__getattribute__name
古明地觉
"""
#age也是相同的逻辑,和name一样,这两个属性都是存在的
print(girl.age)
"""
__getattribute__age
17
"""

#依旧执行__getattribute__,然后调用父类的__getattribute__
#由于属性xxx不存在,于是会执行__getattr__
print(girl.xxx)
"""
__getattribute__xxx
__getattr__xxx
获取属性xxx
"""

那么问题来了,这个 __getattribute__ 有啥用呢?该方法被称为属性拦截器,显然它可以起到一个控制属性访问权限的作用。

classGirl:

def__init__(self):
self.name="古明地觉"
self.age=17

def__getattr__(self,item):
returnf"属性{item}不存在"

def__getattribute__(self,item):
ifitem=="age":
return"女人芳龄不可泄露,别问,问就是还不到18岁"
returnsuper().__getattribute__(item)


girl=Girl()
#name属性存在,所以在__getattribute__中直接返回
print(girl.name)
"""
古明地觉
"""
#age也是如此,也是在__getattribute__中直接返回
#只不过它相当于被拦截了
print(girl.age)
"""
女人芳龄不可泄露,别问,问就是还不到18岁
"""
#父类在执行_http://www.devze.com_getattribute__的时候,发现xxx属性不存在
#于是会触发__getattr__的执行(如果没定义则抛出AttributeError)
print(girl.xxx)
"""
属性xxx不存在
"""

所以 __getattribute__ 就相当于一个属性拦截器,不管获取啥属性,都要先经过它。如果你发现有一些属性不想让外界访问,那么直接拦截掉即可,比如上面代码中的 age 属性。

然后对于那些可以让外界访问的属性,则需要调用父类的 __getattribute__ 帮我们去获取(因为我们手动获取的话会陷入无线递归),并且在获取不存在的属性时也会自动执行 __getattr__。

当然啦,除了属性,方法也是一样的。

classGirl:

def__init__(self):
self.name="古明地觉"
self.age=17

defget_info(self):
returnf"name编程客栈:{self.name},age:{self.age}"

def__getattribute__(self,item):
ifitem=="get_info":
return"此方法禁止获取"
returnsuper().__getattribute__(item)


girl=Girl()
print(girl.get_info)
"""
此方法禁止获取
"""
#默认情况下girl.get_info拿到的是一个方法
#然后再加上小括号就会执行该方法
#但在__getattribute__中我们将其拦截了,并返回一个字符串
#所以此时girl.get_info()就会报错,因为字符串无法被调用

以上内容就是 __getattr__ 和 __getattribute__ 的区别与用法,在工作中看看能不能让它们派上用场。不过说实话,__getattr__ 用的还是蛮频繁的,而 __getattribute__ 则用的不多,至少我就很少用。

到此这篇关于一文解密python中_getattr_和_getattribute_的用法与区别的文章就介绍到这了,更多相关Python getattr getattribute内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号