Python __getattribute__ vs __getattr__ 浅谈

本文探讨了Python中getattr与getattribute的区别及应用。通过自定义getattr实现动态生成URL的优雅解决方案,介绍了getattribute的基本原理及如何避免无限递归。同时讨论了两者结合使用时需要注意的问题。

相信大家看到这个标题的时候也会立马在脑海里面过一遍,觉得大多数时候我们并不太需要关注getattributegetattr的一些细节(至少我自己吧:)),
一般情况下消费我们自定义的类的时候,我们对类的结构都了解,不会刻意偏离,造成一些属性访问的错误。

不过作为一个有好奇心有追求有气质的python宝宝,怎么可能不稍稍研究一下呢。好吧,其实是在github上读到一个开源项目sinaweibopy的源码才看的,代码挺有意思,正好当作一个实用的例子,来看看如何自定义实现gettattr让代码更加的动态优雅:

# 例子在原来的基础上简化了一下,排除依赖和干扰,详细参见原项目
class UrlGenerator(object):
    def __init__(self, root_url):
        self.url = root_url

    def __getattr__(self, item):
        if item == 'get' or item == 'post':
            print self.url
        return UrlGenerator('{}/{}'.format(self.url, item))


url_gen = UrlGenerator('http://xxxx')
url_gen.users.show.get

>>> http://xxxx/users/show

充分利用getattr会在没有查找到相应实例属性时被调用的特点,方便的通过链式调用生成对应的url,源代码中在碰到http method的时候返回一个
可调用的对象更加的优雅,链式的操作不仅优雅而且还能很好的说明调用的接口的意义(restful的接口啦)。

既然能通过定制类的getattr自定义方法来实现一些优雅的功能,自然我们也要对它有一些了解,包括和它相似的自定义方法getattribute

1. 用作实例属性的获取和拦截

当访问某个实例属性时, getattribute会被无条件调用,如未实现自己的getattr方法,会抛出AttributeError提示找不到这个属性,如果自定义了自己getattr方法的话,方法会在这种找不到属性的情况下被调用,比如上面的例子中的情况。所以在找不到属性的情况下通过实现自定义的getattr方法来实现一些功能是一个不错的方式,因为它不会像getattribute方法每次都会调用可能会影响一些正常情况下的属性访问:

class Test(object):
    def __init__(self, p):
        self.p = p

    def __getattr__(self, item):
        return 'default'

t = Test('p1')
print t.p
print t.p2

>>> p1
>>> default
2. 自定义getattribute的时候防止无限递归

因为getattribute在访问属性的时候一直会被调用,自定义的getattribute方法里面同时需要返回相应的属性,通过self.__dict__取值会继续向下调用getattribute,造成循环调用:

class AboutAttr(object):
    def __init__(self, name):
        self.name = name

    def __getattribute__(self, item):
        try:
            return super(AboutAttr, self).__getattribute__(item)
        except KeyError:
            return 'default'

这里通过调用绑定的super对象来获取队形的属性,对新式类来说其实和object.__getattribute__(self, item)一样的道理:

  • 默认情况下自定义的类会从object继承getattribute方法,对于属性的查找是完全能用的
  • getattribute的实现感觉还是挺抽象化的,只需要绑定相应的实例对象和要查找的属性名称就行
3.同时覆盖掉getattribute和getattr的时候,在getattribute中需要模仿原本的行为抛出AttributeError或者手动调用getattr
class AboutAttr(object):
    def __init__(self, name):
        self.name = name

    def __getattribute__(self, item):
        try:
            return super(AboutAttr, self).__getattribute__(item)
        except KeyError:
            return 'default'
        except AttributeError as ex:
            print ex

    def __getattr__(self, item):
        return 'default'

at = AboutAttr('test')
print at.name
print at.not_exised

>>>test
>>>'AboutAttr' object has no attribute 'not_exised'
>>>None

上面例子里面的getattr方法根本不会被调用,因为原本的AttributeError被我们自行处理并未抛出,也没有手动调用getattr,所以访问not_existed的结果是None而不是default.

关于getattribute和getattr的特性与区别就扯到这,要更深入的了解可以自行google,编写高质量代码:改善Python程序的91个建议这本书里面也有一些详细的介绍。



文/TypingQuietly(简书作者)
原文链接:http://www.jianshu.com/p/885d59db57fc
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
### Python 中 `__getattr__` 方法的用法 在 Python 的类定义中,`__getattr__` 是一种特殊的方法,用于处理访问不存在的属性的情况。当尝试访问对象的一个未定义属性时,如果该类实现了 `__getattr__` 方法,则会调用此方法来动态返回值或执行特定逻辑[^1]。 以下是关于 `__getattr__` 方法的一些重要特性和使用场景: #### 特性描述 - 当通过点号操作符(`.`)访问一个实例的属性而找不到对应名称时,Python 会在抛出 `AttributeError` 前自动调用 `__getattr__` 方法。 - 需要注意的是,只有在常规属性查找失败的情况下才会触发 `__getattr__` 方法。因此,在类中显式定义的属性不会进入这个机制[^2]。 #### 使用示例 下面是一个简单的例子展示如何自定义行为以应对未知属性请求: ```python class DynamicAttributes: def __init__(self, initial=None): if initial is None: self.data = {} else: self.data = dict(initial) def __getattr__(self, name): """Called when an attribute lookup has not found the attribute in the usual places.""" try: return self.data[name] except KeyError as e: raise AttributeError(f"No such attribute: {e}") example = DynamicAttributes({"key": "value"}) print(example.key) # 输出 'value' # 下面这行将会引发 AttributeError 因为字典里没有对应的键 # print(example.nonexistent_key) ``` 在这个例子中,我们创建了一个名为 `DynamicAttributes` 的类,它允许存储任意数量的关键字参数作为内部数据结构的一部分,并且可以通过普通的点记法访问这些关键字所代表的数据项。如果没有找到指定的名字,则会抛出异常表明缺少这样的特性[^3]。 另外需要注意一点区别:虽然两者都涉及到了获取成员的操作,但是 `__getattribute__` 函数几乎适用于所有的属性检索过程;相比之下,`__getattr__` 只有在标准途径无法定位到目标的时候才被激活[^4]。 #### 单例模式中的应用举例 有时可以利用 `__getattr__` 来辅助构建某些设计模式下的解决方案,比如单例模式。尽管这里给出的例子主要依赖于装饰器完成任务,但我们同样能够借助类似的思路结合 `__getattr__` 达成目的[^5]: ```python class SingletonMeta(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: instance = super().__call__(*args, **kwargs) cls._instances[cls] = instance return cls._instances[cls] def __getattr__(cls, item): if hasattr(SingletonMeta._instances.get(cls), item): return getattr(SingletonMeta._instances.get(cls), item) raise AttributeError("No such attribute exists.") class MySingleton(metaclass=SingletonMeta): pass first_instance = MySingleton() second_instance = MySingleton() assert first_instance is second_instance ``` 在此案例中展示了另一种形式的单例实现方式——元编程技术。其中也体现了对于非正式存在的字段查询进行了重定向处理的方式[^6]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值