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

 更新时间:2023年01月13日 14:52:28   作者:古明地觉  
这篇文章主要为大家详细介绍了Python中_getattr_和_getattribute_的用法与区别,文中通过一些简单的示例为大家进行了讲解,需要的可以参考一下

__getattr__

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

class A:
    pass

a = A()
a.xxx
"""
AttributeError: 'A' object has no attribute 'xxx'
"""

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

class Girl:

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

    def get_info(self):
        return f"name: {self.name}, age: {self.age}"

    def __getattr__(self, item):
        return f"你访问了 {item} 属性"


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

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

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

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

def __getattr__(name):
    return f"{__name__} 中不存在 {name}"

name = "古明地觉"
age = 17

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

from tools import name, age, xxx, yyy

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

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

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

__getattribute__

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

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

我们举个例子:

class Girl:

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

    def __getattribute__(self, item):
        return f"获取属性: {item}"


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

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

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

class Girl:

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

    def __getattribute__(self, item):
        return getattr(self, item)


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

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

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

class Girl:

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

    def __getattribute__(self, item):
        return super().__getattribute__(item)


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

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

class Girl:

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

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

    def __getattribute__(self, item):
        print(f"__getattribute__ {item}")
        return super().__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__ 有啥用呢?该方法被称为属性拦截器,显然它可以起到一个控制属性访问权限的作用。

class Girl:

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

    def __getattr__(self, item):
        return f"属性 {item} 不存在"

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


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

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

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

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

class Girl:

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

    def get_info(self):
        return f"name: {self.name}, age: {self.age}"

    def __getattribute__(self, item):
        if item == "get_info":
            return "此方法禁止获取"
        return super().__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内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • python模拟登录并且保持cookie的方法详解

    python模拟登录并且保持cookie的方法详解

    模拟登录相信对大家来说都不陌生,下面这篇文章主要给大家介绍了关于python模拟登录并且保持cookie的方法,文中介绍的非常详细,对大家具有一定的参考价值,需要的朋友们下面来一起看看吧。
    2017-04-04
  • Python如何自定义邻接表图类

    Python如何自定义邻接表图类

    这篇文章主要介绍了Python如何自定义邻接表图类问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-12-12
  • 基于Python第三方插件实现西游记章节标注汉语拼音的方法

    基于Python第三方插件实现西游记章节标注汉语拼音的方法

    这篇文章主要介绍了基于Python第三方插件实现西游记章节标注汉语拼音的方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-05-05
  • python3序列化与反序列化用法实例

    python3序列化与反序列化用法实例

    这篇文章主要介绍了python3序列化与反序列化用法,实例分析了Python3使用pickle模块针对字符串进行序列化操作的相关技巧,需要的朋友可以参考下
    2015-05-05
  • python简单利用字典破解zip文件口令

    python简单利用字典破解zip文件口令

    这篇文章主要给大家介绍了关于python简单利用字典破解zip文件口令的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • python安装第三方库如xlrd的方法

    python安装第三方库如xlrd的方法

    这篇文章主要介绍了python安装第三方库如xlrd的方法,本文通过两种方法给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-10-10
  • Python自动修改电脑静态IP地址的实现示例

    Python自动修改电脑静态IP地址的实现示例

    通过Python自动修改电脑的静态IP地址可以极大地提高我们的工作效率,减少手动修改IP地址带来的错误,本文就来介绍一下Python自动修改电脑静态IP地址的实现示例,感兴趣的可以了解一下
    2023-11-11
  • 在pytorch 中计算精度、回归率、F1 score等指标的实例

    在pytorch 中计算精度、回归率、F1 score等指标的实例

    今天小编就为大家分享一篇在pytorch 中计算精度、回归率、F1 score等指标的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-01-01
  • python读取查看npz/npy文件数据以及数据完全显示方法实例

    python读取查看npz/npy文件数据以及数据完全显示方法实例

    前两天从在GitHub下载了一个代码,其中的数据集是.npz结尾的文件,之前没有见过不知道如何处理,下面这篇文章主要给大家介绍了关于python读取查看npz/npy文件数据以及数据完全显示方法的相关资料,需要的朋友可以参考下
    2022-04-04
  • 基于python分享极坐标下的几类典型曲线

    基于python分享极坐标下的几类典型曲线

    这篇文章主要介绍了基于python分享极坐标下的几类典型曲线,极坐标系统是一套区别于笛卡尔直角坐标系的二维坐标系统,下面我们在python的基础上讲解及坐标及其下的几种曲线,需要的小伙伴可以参考一下
    2022-03-03

最新评论