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

   2023-02-08 学习力0
核心提示:目录__getattr____getattribute____getattr__当访问实例对象的某个不存在的属性时,毫无疑问会报错,会抛出 AttributeError。class A:    passa = A()a.xxx"""AttributeError: 'A' object has no attribute 'xxx'"""但如果我们希望在找不到某个

__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__ 则用的不多,至少我就很少用。

原文地址:https://mp.weixin.qq.com/s/A964QFZgzChA0YVrQ8K-pg
 
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • 如何在Abaqus的python中调用Matlab程序
    目录1. 确定版本信息2. 备份python3. 设置环境变量4. 安装程序5. 调试运行参考资料Abaqus2018操作系统Win10 64位Python版本2.7(路径C:\SIMULIA\CAE\2018\win_b64\tools\SMApy\python2.7)2. 备份python将上述的“python2.7”文件夹复制出来,避免因操作错误
    03-16
  • SICP:复数的直角和极坐标的表示(Python实现)
    SICP:复数的直角和极坐标的表示(Python实现)
    数据抽象屏障是控制复杂性的强有力工具,然而这种类型的数据抽象还不够强大有力。从一个另一个角度看,对于一个数据对象可能存在多种有用的表示方式,且我们希望所设计的系统能够处理多种表示形式。比如,复数就可以表示为两种几乎等价的形式:直角坐标形式(
    03-16
  • [个人发展] 我做了一个可以永远谈论任何事情的女士对话AI(TypeScript,Python)
    [个人发展] 我做了一个可以永远谈论任何事情的
    在个人发展中对话式人工智能服务 Eveki我做了虚构角色1这是一项以人工智能为特色的服务,可以再现并享受自然对话。这一次,作为第一个艾小姐发表了。请先尝试实物。服务概览与人工智能对话基本上只需输入您的信息是。对话是用女士的语言进行的,就像人类一样
    03-08
  • ruby写爬虫 ruby python
    ruby写爬虫 ruby python
    http://www.javaeye.com/topic/545160爬虫性能比较http://www.rubyrailways.com/data-extraction-for-web-20-screen-scraping-in-rubyrails/srcapihttp://huacnlee.com/blog/ruby-scrapi-collect-koubei  2009年4月22日 星期三用ruby写的一个网络爬虫程序前
    03-08
  • sf02_选择排序算法Java Python rust 实现
    Java 实现package common;public class SimpleArithmetic {/** * 选择排序 * 输入整形数组:a[n] 【4、5、3、7】 * 1. 取数组编号为i(i属于[0 , n-2])的数组值 a[i],即第一重循环 * 2. 假定a[i]为数组a[k](k属于[i,n-1])中的最小值a[min],即执行初始化 min =i
    02-09
  • Python vs Ruby: 谁是最好的 web 开发语言?
    Python 和 Ruby 都是目前用来开发 websites、web-based apps 和 web services 的流行编程语言之一。 这两种语言在许多方面有相似之处。它们都是高级的面向对象的编程语言,都是交互式脚本语言、都提供标准库且支持持久化。但是,Python 和 Ruby 的解决方法却
    02-09
  • 详解Python手写数字识别模型的构建与使用
    详解Python手写数字识别模型的构建与使用
    目录一:手写数字模型构建与保存1 加载数据集2 特征数据 标签数据3 训练集 测试集4 数据流图 输入层5 隐藏层6 损失函数7 梯度下降算法8 输出损失值 9 模型 保存与使用10 完整源码分享二:手写数字模型使用与测试一:手写数字模型构建与保存1 加载数据集# 1加
  • Python asyncore socket客户端实现方法详解
    Python asyncore socket客户端实现方法详解
    目录介绍1.定义类并且继承 asyncore.dispatcher2.实现类中的回调代码调用父类方法创建socket对象连接服务器3.创建对象并且执行asyncore.loop进入运行循环服务端示例代码运行结果注意介绍asyncore库是python的一个标准库,提供了以异步的方式写入套接字服务的
  • Python+Sklearn实现异常检测
    目录离群检测 与 新奇检测Sklearn 中支持的方法孤立森林 IsolationForestLocal Outlier FactorOneClassSVMElliptic Envelope离群检测 与 新奇检测很多应用场景都需要能够确定样本是否属于与现有的分布,或者应该被视为不同的分布。离群检测(Outlier detectio
  • Python基础教程之while循环用法讲解 Python中的while循环
    Python基础教程之while循环用法讲解 Python中的
    目录1.while 循环2.无限循环3、while 循环使用 else 语句4、简单语句组附小练习:总结1.while 循环Python 中 while 语句的一般形式:while 判断条件(condition):    执行语句(statements)……执行流程图如下:同样需要注意冒号和缩进。另外,在 Python 中
点击排行