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
好了,今天的內容就到這。