getattr, __getattr__, __getattribute__和__get__区别

文章详细介绍了Python中用于属性和方法访问的几个关键函数:getattr()用于安全地获取对象属性,__getattr__和__getattribute__在访问不存在的属性时发挥作用,前者在__getattribute__找不到属性时被调用,而__get__是描述符协议的一部分,控制描述符对象的获取行为。通过示例代码展示了这些函数和方法的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、getattr()

和另外三个方法都是魔法函数不同的是,getattr()是python内置的一个函数,它可以用来获取对象的属性和方法。例子如下:

class A():
    a = 5
    def __init__(self, x):
        self.x = x

    def hello(self):
        return 'hello func'

a = A(10)

print(getattr(a, 'x'))  #相当于a.x
print(getattr(a, 'y', 20))  #相当于a.y,因为a.y并不存在,所以返回第三个参数作为默认值
print(getattr(a, 'hello')())  # 相当于a.hello()

print(getattr(A, 'a'))  # 相当于A.a

这段代码的输出结果是:

10
20
hello func
5

可以看出,getattr()可以用来获取对像的属性和方法,需要注意的是,如果通过getattr()来尝试获取对象里并不存在的属性时没有添加第三个默认值,代码会 报错,如下所示:

print(getattr(a, 'y'))

运行会报异常提示找不到属于y:

Traceback (most recent call last):
  File "app/test.py", line 32, in <module>
    print(getattr(a, 'y'))
AttributeError: 'A' object has no attribute 'y'

二、__getattr()与__getattribute()

这两个是类对象的魔法函数,在访问对象属性的时候会被调用,但是两者之间也有一点区别, 我们通过代码来看一下:

class A(object):
  def __init__(self, x):
    self.x = x

  def hello(self):
    return 'hello func'

  def __getattr__(self, item):
    print('in __getattr__')
    return 100

  def __getattribute__(self, item):
    print('in __getattribute__')
    return super(A, self).__getattribute__(item)

a = A(10)
print(a.x)
print(a.y)

运行代码,得到下面输出:

in __getattribute__
10
in __getattribute__
in __getattr__
100

可以看出,在获到对象属性时,__getattribute__()是一定会被调用的,无论属性存不存在,首先都会调用这个魔法方法。 如果调用像a.y这种不存在的对象时,调用__getattribute__()找不到y这个属性,就会再调用__getattr__()这个魔法方法,可以通过在这个方法里实 来设置属性不存在时的默认值。使用上面的getattr()方法获取属性时,也是同样的调用关系,只不过只有在getattr()带第三个参数作为默认值时,才会调用 __getattr__()方法。

三、__get__()

__get__() 是一个描述符协议(Descriptor Protocol)中的魔法函数,用于定义描述符对象的获取行为。描述符是一种特殊的对象,用于控制对属性的访问。描述符可以被用作类的属性,通过访问和设置描述符属性,可以触发 __get__()__set__() 和 __delete__() 等方法。

在描述符协议中,当通过实例访问描述符属性时,解释器会自动调用描述符对象的 __get__() 方法,并传入三个参数:

  • self: 描述符对象自身的实例。
  • instance: 调用描述符的实例对象。
  • owner: 描述符所属的类对象。

__get__() 方法的返回值将作为属性的值返回给调用方。

下面是一个简单的示例,演示了如何使用 __get__() 方法定义一个描述符:

class Descriptor:
    def __get__(self, instance, owner):
        print("Getting the value")
        return instance._value

    def __set__(self, instance, value):
        print("Setting the value")
        instance._value = value

class MyClass:
    descriptor = Descriptor()

    def __init__(self, value):
        self._value = value

my_obj = MyClass(42)
print(my_obj.descriptor)  # 输出: Getting the value, 42
my_obj.descriptor = 100   # 输出: Setting the value
print(my_obj.descriptor)  # 输出: Getting the value, 100

 

在上述示例中,Descriptor 类是一个描述符,它定义了 __get__() 和 __set__() 方法。MyClass 类中的 descriptor 属性是一个描述符实例。当通过 my_obj.descriptor 访问该属性时,会触发 Descriptor 类中的 __get__() 方法,并返回 _value 属性的值。当给 my_obj.descriptor 赋值时,会触发 Descriptor 类中的 __set__() 方法,将新的值赋给 _value 属性。

需要注意的是,描述符协议还包括 __set__() 和 __delete__() 方法,用于定义设置和删除描述符属性的行为。这些方法可以根据需要进行实现,以控制描述符属性的行为。

 

参考:

getattr, __getattr__, __getattribute__和__get__区别 - 知乎

<think>好的,用户问的是Python中__get__、__getattr____getattribute__的区别。这个问题涉及到Python的描述符协议属性访问机制,我得仔细理清楚每个方法的作用调用时机。 首先,__get__是描述符协议的一部分。描述符通常用于管理类属性的访问,比如在@property装饰器背后就是用到了描述符。当一个类属性被定义为描述符时,每次访问该属性都会触发__get__方法。需要区分实例访问类访问的不同情况,比如通过实例访问时,__get__的参数是实例类,而通过类访问时,实例参数为None。这点需要注意。 接下来是__getattr__。这个方法在属性查找失败时被调用。也就是说,当访问一个不存在的属性时,Python会调用__getattr__。这个方法适合用来实现动态属性或者延迟加载。例如,如果用户访问obj.undefined_attr,而该属性不存在,就会触发__getattr__。需要注意的是,如果属性存在于实例或类中,这个方法不会被调用。 然后是__getattribute__。这个方法在每次访问属性时都会被调用,无论属性是否存在。它拦截所有属性访问,因此在使用时需要特别小心,避免无限递归。通常,在覆盖__getattribute__时,应该通过调用object类的__getattribute__来获取属性,比如使用object.__getattribute__(self, name)。 用户可能需要知道这些方法的调用顺序适用场景。比如,__getattribute__总是被调用,如果在这里抛出AttributeError,才会触发__getattr__。而__get__则是在属性被访问时,如果该属性是一个描述符实例,才会被调用。需要举一些例子来说明不同的情况,比如使用描述符的类,或者动态生成属性的情况。 还需要注意常见的错误,比如在__getattribute__中直接访问self.name会导致递归调用,必须通过object.__getattribute__来避免。同时,__getattr__只有在属性不存在时才会被调用,所以它适合处理缺失属性的情况。 另外,用户可能在理解这三者的区别时容易混淆,特别是__get__另外两个方法,因为它们属于不同的机制。需要明确__get__属于描述符协议,用于管理类属性的访问,而__getattr____getattribute__属于实例的属性访问机制。 总结时,要分点说明每个方法的作用、调用时机典型应用场景,帮助用户清晰区分。同时,提供示例代码会更有助于理解,比如定义一个描述符类,展示__get__的使用,或者覆盖__getattr__来动态生成属性。 可能还需要回答相关问题,比如如何正确使用这些方法,避免常见错误,或者它们在实际项目中的应用案例。比如,Django模型中的字段可能使用描述符来管理数据库访问,或者在动态代理中使用__getattr__来转发方法调用。 最后,确保回答结构清晰,逐步解释每个方法,并对比它们的差异,帮助用户逐步建立正确的理解。</think>### Python中__get__、__getattr____getattribute__的区别 #### 1. **`__get__`:描述符协议** - **作用**:用于实现**描述符协议**,主要用于类属性级别的访问控制。当某个类属性被定义为描述符时(例如`@property`装饰器底层实现),访问该属性会触发`__get__`方法。 - **调用时机**:当通过实例或类访问描述符属性时触发。 - **参数**:`def __get__(self, instance, owner)`,其中`instance`是实例对象,`owner`是拥有该属性的类。 - **示例**: ```python class Descriptor: def __get__(self, instance, owner): print("调用__get__") return "值" class MyClass: attr = Descriptor() obj = MyClass() print(obj.attr) # 输出 "调用__get__" 后返回 "值" ``` #### 2. **`__getattr__`:属性缺失处理** - **作用**:在**属性查找失败时触发**,用于动态处理不存在的属性。 - **调用时机**:当访问的属性不存在于实例或类中时触发。 - **参数**:`def __getattr__(self, name)`,`name`为属性名。 - **典型场景**:动态生成属性、延迟加载。 - **示例**: ```python class DynamicAttributes: def __getattr__(self, name): print(f"动态生成属性 {name}") setattr(self, name, "默认值") return "默认值" obj = DynamicAttributes() print(obj.new_attr) # 输出 "动态生成属性 new_attr" 后返回 "默认值" ``` #### 3. **`__getattribute__`:全属性拦截** - **作用**:**拦截所有属性访问**,无论属性是否存在。 - **调用时机**:每次访问属性时触发(包括已存在的属性)。 - **参数**:`def __getattribute__(self, name)`。 - **注意事项**: - 必须通过`object.__getattribute__(self, name)`访问属性,否则会陷入无限递归。 - 覆盖此方法需谨慎,可能显著影响性能。 - **示例**: ```python class Logger: def __getattribute__(self, name): print(f"访问属性 {name}") return object.__getattribute__(self, name) obj = Logger() obj.x = 10 print(obj.x) # 输出 "访问属性 x" 后返回 10 ``` --- ### 对比总结 | 方法 | 触发条件 | 用途 | 优先级 | |---------------------|----------------------------|--------------------------------------|---------------| | `__get__` | 访问描述符类属性时 | 实现属性计算、验证 | 描述符协议优先 | | `__getattr__` | 属性不存在时 | 动态属性处理、错误兜底 | 次优先级 | | `__getattribute__` | 所有属性访问 | 全局属性控制、日志记录 | 最高优先级 | --- ### 常见问题 #### 1. **如何避免`__getattribute__`中的无限递归?** - 必须使用`object.__getattribute__(self, name)`访问属性,而非直接`self.name`。例如: ```python def __getattribute__(self, name): # 错误写法:return self.__dict__[name] → 递归调用 return object.__getattribute__(self, name) # 正确 ``` #### 2. **`__getattr__``__getattribute__`的调用顺序?** - 若`__getattribute__`抛出`AttributeError`,则触发`__getattr__`[^1]。 #### 3. **描述符的实际应用场景?** - 例如在Django模型中,字段通过描述符实现数据库字段与Python属性的映射: ```python class Field: def __get__(self, instance, owner): return instance.__dict__[self.name] ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值