python魔法函數__dict__和__getattr__的妙用

__dict__

__dict__是用來存儲對象屬性的一個字典,其鍵爲屬性名,值爲屬性的值。

既然__dict__是個字典那麼我們就可以用字典的屬性了。

我們通過使用dir()屬性來看看__dict__都有哪些屬性。

['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']

我們看一段代碼內含註釋:

class A():
    def __init__(self):
        self.name="liming"

    def save_data(self,dicts):
        self.__dict__.update(dicts)#添加字典元素
        if isinstance(self.__dict__,dict):
            print(True)
        #獲取字典獨有的屬性
        print(set(dir(self.__dict__))-set(dir(self)))
        return self.__dict__

if __name__ == '__main__':
     dicts={"a":1,"b":2,"c":3}
     a=A()
     print(a.save_data(dicts))

輸出結果

True
{'__delitem__', 'keys', 'update', '__len__', '__getitem__', 'get', 'clear', 'copy', 'popitem', '__iter__', 'items', '__contains__', 'pop', '__setitem__', 'fromkeys', 'values', 'setdefault'}
{'name': 'liming', 'a': 1, 'b': 2, 'c': 3}

下面來一個比較實用的例子來大大的減少你的代碼,做到真正的pythonic。
我們在使用給對象的屬性賦值的時候

class A():
    def __init__(self,dicts):
        self.name=dicts["name"]
        self.age=dicts["age"]
        self.sex=dicts["sex"]
        self.hobby=dicts["hobby"]
if __name__ == '__main__':
     dicts={"name":"lisa","age":23,"sex":"women","hobby":"hardstyle"}
     a=A(dicts)

我們看到我們需要換取傳入的字典的各個鍵值,並創建鍵值同名一個屬性,這裏我們只有4個還好,想象一下如果我們傳入的字典有100個鍵。。。如何還是這樣一個一個賦值不敢想不敢想,人家都寫完代碼了,你還在賦值有木有。。
其實一開始的那段代碼已經給出了答案,如果不會也沒關係,
下面我們就來點pythonic的python。來解決這個問題。
上面代碼簡化爲:

class A():
    def __init__(self,dicts):
        self.__dict__.update(dicts)
        print(self.__dict__)

if __name__ == '__main__':
     dicts={"name":"lisa","age":23,"sex":"women","hobby":"hardstyle"}
     a=A(dicts)

看完後感覺怎麼樣啊,其實__dict__還有一個重要的用處就是單例模式中共享同一狀態,參考之前寫的單例模式。
拓展:部分內建函數不包含__dict__屬性比如list,如果要查看list的屬性怎麼辦呢,這時候用dir(list),dir方法也是查看對象的屬性,包括內建對象的屬性,但是它的輸出形式列表,而__dict__是列表。

__getattr__

經過查閱資料用我的理解去解釋這個方法的用法那就是:使用.獲取屬性的時候,如果該屬性存在就輸出其值,如果不存在則會去找__getatrr__,我們可以通過重寫該方法可以實現動態屬性的操作。(如果只允許添加指定的屬性需要用__solts__函數控制,這裏不做詳細講解)

先來一段比較有意思的代碼

from requests_html import HTMLSession
class UrlGenerator(object):
    def __init__(self, root_url):
        self.url = root_url
        self.session=HTMLSession()

    def __getattr__(self, item):
        if item == 'get':
            self.get_html()
        return UrlGenerator('{}.{}'.format(self.url, item))
    def get_html(self):
        req = self.session.get(self.url)
        print(req.text)

url_gen = UrlGenerator('https://www')
url_gen.baidu.com.get

充分利用__getattr__會在沒有查找到相應實例屬性時被調用的特點,方便的通過鏈式調用生成對應的url,在碰到get方法的時候調用函數獲取其網頁源碼。
可調用的對象更加的優雅,鏈式的操作不僅優雅而且還能很好的說明調用的接口的意義。

下面展示一個__getattr__經典應用的例子,可以通過獲取屬性值的方式獲取字典的鍵值。

class ObjectDict(dict):
    def __init__(self, *args, **kwargs):
        super(ObjectDict, self).__init__(*args, **kwargs)

    def __getattr__(self, name):
        value = self[name]
        if isinstance(value, dict):
            value = ObjectDict(value)
        return value

if __name__ == '__main__':
    od = ObjectDict(asf={'a': 1}, d=True)
    print(od.asf,od.asf.a)     # {'a': 1} 1
    print(od.d)                 # True

好了,今天的內容就到這。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章