python __getattr__ 與 __getattribute__

定義區別:

  • _getattr_(self, item)
    獲取實例的屬性時,僅當實例屬性中不包括item時,才被調用。這個方法應該返回相應的屬性值或者拋出 AttributeError 異常

  • _getattribute_(self, item)
    獲取實例屬性時,無條件調用該方法,不論實例屬性中是否包括item

應用實例

  1. 利用__getattr__結合遞歸實現url動態生成器,代碼來自于github-sinaweibopy
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

  1. 自己的django項目中,結合__getattribute__與decorator實現動態獲取屬性。
myconf = MyConfClient()

def myconf_decorator(key):
"""
首先從myconf中獲取屬性key,取不到再到origin_class取
"""
    def wraper(origin_class):
        # 定義被裝飾類origin_class的__getattribute__方法
        def __getattribute__(self, item):
            try:
                # 首先從myconf中獲取屬性
                return myconf.get_dict(key)[item]
            except (CanNotGetConfError, KeyError):
                try:
                    # myconf中不存在屬性key時,再從origin_class中查找
                    return self.item
                except KeyError:
                    raise CanNotGetConfError('key: %s item: %s' % (key, item))
        origin_class.__getattribute__ = __getattribute__
        return origin_class()
    return wraper

在寫這個裝飾器的過程中,出現了無限遞歸的情況!!

RecursionError: maximum recursion depth exceeded while calling a Python object

_getattribute_無限遞歸

__getattribute__: 是無條件被調用。對任何對象的屬性訪問時,都會隱式的調用__getattribute__方法,比如調用t.__dict__,其實執行了t.__getattribute__("__dict__")方法.

例如存在類A,a是A的一個實例,當我們通過a.x獲取屬性值時,實際的過程:

  • 如果類A中重載了__getattribute__,則調用__getattribute
  • 沒有重載__getattribute__,則調用a.\__dict__[x]
  • 調用A.\__dict__[x]或者A.\__base__.__dict[x]

所以按照上面的寫法,我們在a.x時,調用__getattribute__方法,而該方法中使用self.key,相當于再次調用__getattribute__,造成無限遞歸。或者我們在重載__getattribute__中又調用__dict__的話,也會無限遞歸。

so,解決方法,在__getatribute__方法中,調用object.__getattribute__,切斷無限遞歸:

def __getattribute__(self, item):
    try:
        # 首先從myconf中獲取屬性
        return myconf.get_dict(key)[item]
    except (CanNotGetConfError, KeyError):
        try:
            # myconf中不存在屬性key時,再從origin_class中查找
            return super(origin_class, self).__getattribute__(item)
        except (KeyError, AttributeError):
            raise CanNotGetConfError('key: %s item: %s' % (key, item))

而且如果沒有重載__getattr__方法時,__getattribute__方法中需要處理AttributeError異常

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容