您好,登錄后才能下訂單哦!
小編給大家分享一下Python如何獲取對象屬性,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
Python是一種編程語言,內置了許多有效的工具,Python幾乎無所不能,該語言通俗易懂、容易入門、功能強大,在許多領域中都有廣泛的應用,例如最熱門的大數據分析,人工智能,Web開發等。
假如有以下的類:
class Person(object): def __init__(self, name, age): self.name = name self.age = age def __str__(self): return 'name=%s, age=%s' % (self.name, self.age)
方法一:使用屬性運算符
print(xmr.name)
方法二:通過屬性字典__dict__
print(xmr.__dict__['name'])
方法三:通過getattr函數
print(getattr(xmr, 'name'))
方法四:operator.attrgetter
import operator op = operator.attrgetter('name') print(op(xmr))
方法四可以用于對象的排序,比如需要根據年齡age來排序Person對象:
import operator p_list = [Person('xiemanR', 18), Person('zhangshan', 17), Person('lisi', 20), Person('wangwu', 25)] r = sorted(p_list, key=operator.attrgetter('age')) for i in r: print(i)
輸出結果:
Person(name=zhangshan, age=17) Person(name=xiemanR, age=18) Person(name=lisi, age=20) Person(name=wangwu, age=25)
PS:其實第四種方法是調用第三種方法的,只是簡單封裝了一下,我們看看operator.attrgetter實現就知道了:
class attrgetter: def __init__(self, attr, *attrs): if not attrs: if not isinstance(attr, str): raise TypeError('attribute name must be a string') names = attr.split('.') def func(obj): for name in names: obj = getattr(obj, name) return obj self._call = func else: getters = tuple(map(attrgetter, (attr,) + attrs)) def func(obj): return tuple(getter(obj) for getter in getters) self._call = func def __call__(self, obj): return self._call(obj)
完。
補充知識:深入理解python對象及屬性
類屬性和實例屬性
首先來看看類屬性和類實例的屬性在python中如何存儲,通過__dir__方法來查看對象的屬性
>>> class Test(object): pass >>> test = Test() # 查看類屬性 >>> dir(Test) ['__class__','__delattr__','__dict__','__doc__','__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__'] # 查看實例屬性 >>> dir(test) ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
我們主要看一個屬性__dict__,因為 __dict__保存的對象的屬性,看下面一個例子
>>> class Spring(object): ... season = "the spring of class" ... # 查看Spring類保存的屬性 >>> Spring.__dict__ dict_proxy({'__dict__': <attribute '__dict__' of 'Spring' objects>, 'season': 'the spring of class', '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'Spring' objects>, '__doc__': None}) # 通過兩種方法訪問類屬性 >>> Spring.__dict__['season'] 'the spring of class' >>> Spring.season 'the spring of class'
發現__dict__有個'season'鍵,這就是這個類的屬性,其值就是類屬性的數據.
接來看,看看它的實例屬性
>>> s = Spring() # 實例屬性的__dict__是空的 >>> s.__dict__ {} # 其實是指向的類屬性 >>> s.season 'the spring of class' # 建立實例屬性 >>> s.season = "the spring of instance" # 這樣,實例屬性里面就不空了。這時候建立的實例屬性和類屬性重名,并且把它覆蓋了 >>> s.__dict__ {'season': 'the spring of instance'} >>> s.__dict__['season'] 'the spring of instance' >>> s.season 'the spring of instance' # 類屬性沒有受到實例屬性的影響 >>> Spring.__dict__['season'] 'the spring of class' >>> Spring.__dict__ dict_proxy({'__dict__': <attribute '__dict__' of 'Spring' objects>, 'season': 'the spring of class', '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'Spring' objects>, '__doc__': None}) # 如果將實例屬性刪除,又會調用類屬性 >>> del s.season >>> s.__dict__ {} >>> s.season 'the spring of class' # 自定義實例屬性,對類屬性沒有影響 >>> s.lang = "python" >>> s.__dict__ {'lang': 'python'} >>> s.__dict__['lang'] 'python' # 修改類屬性 >>> Spring.flower = "peach" >>> Spring.__dict__ dict_proxy({'__module__': '__main__', 'flower': 'peach', 'season': 'the spring of class', '__dict__': <attribute '__dict__' of 'Spring' objects>, '__weakref__': <attribute '__weakref__' of 'Spring' objects>, '__doc__': None}) >>> Spring.__dict__['flower'] 'peach' # 實例中的__dict__并沒有變化 >>> s.__dict__ {'lang': 'python'} # 實例中找不到flower屬性,調用類屬性 >>> s.flower 'peach'
下面看看類中包含方法,__dict__如何發生變化
# 定義類 >>> class Spring(object): ... def tree(self, x): ... self.x = x ... return self.x ... # 方法tree在__dict__里面 >>> Spring.__dict__ dict_proxy({'__dict__': <attribute '__dict__' of 'Spring' objects>, '__weakref__': <attribute '__weakref__' of 'Spring' objects>, '__module__': '__main__', 'tree': <function tree at 0xb748fdf4>, '__doc__': None}) >>> Spring.__dict__['tree'] <function tree at 0xb748fdf4> # 建立實例,但是__dict__中沒有方法 >>> t = Spring() >>> t.__dict__ {} # 執行方法 >>> t.tree("xiangzhangshu") 'xiangzhangshu' # 實例方法(t.tree('xiangzhangshu'))的第一個參數(self,但沒有寫出來)綁定實例 t,透過 self.x 來設定值,即給 t.__dict__添加屬性值。 >>> t.__dict__ {'x': 'xiangzhangshu'} # 如果沒有將x 賦值給 self 的屬性,而是直接 return,結果發生了變化 >>> class Spring(object): ... def tree(self, x): ... return x >>> s = Spring() >>> s.tree("liushu") 'liushu' >>> s.__dict__ {}
需要理解python中的一個觀點,一切都是對象,不管是類還是實例,都可以看成是對象,符合object.attribute ,都會有自己的屬性
使用__slots__優化內存使用
默認情況下,python在各個實例中為名為__dict__的字典里存儲實例屬性,而字典會消耗大量內存(字典要使用底層散列表提升訪問速度), 通過__slots__類屬性,在元組中存儲實例屬性,不用字典,從而節省大量內存
# 在類中定義__slots__屬性就是說這個類中所有實例的屬性都在這兒了,如果幾百萬個實例同時活動,能節省大量內存 >>> class Spring(object): ... __slots__ = ("tree", "flower") ... # 仔細看看 dir() 的結果,還有__dict__屬性嗎?沒有了,的確沒有了。也就是說__slots__把__dict__擠出去了,它進入了類的屬性。 >>> dir(Spring) ['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'flower', 'tree'] >>> Spring.__slots__ ('tree', 'flower') # 實例化 >>> t = Spring() >>> t.__slots__ ('tree', 'flower') # 通過類賦予屬性值 >>> Spring.tree = "liushu" # tree這個屬性是只讀的, 實例不能修改 >>> t.tree = "guangyulan" Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Spring' object attribute 'tree' is read-only >>> t.tree 'liushu' # 對于用類屬性賦值的屬性,只能用來修改 >>> Spring.tree = "guangyulan" >>> t.tree 'guangyulan' # 對于沒有用類屬性賦值的屬性,可以通過實例來修改 >>> t.flower = "haitanghua" >>> t.flower 'haitanghua' # 實例屬性的值并沒有傳回到類屬性,你也可以理解為新建立了一個同名的實例屬性 >>> Spring.flower <member 'flower' of 'Spring' objects> # 如果再給類屬性賦值 >>> Spring.flower = "ziteng" >>> t.flower 'ziteng'
如果使用的當,__slots__可以顯著節省內存,按需要注意一下問題
在類中定義__slots__之后,實例不能再有__slots__所列名稱之外的其他屬性
每個子類都要定義__slots__熟悉,因為解釋器會忽略繼承__slots__屬性
如果不把__werkref__加入__slots__,實例不能作為弱引用的目標
屬性的魔術方法
來看幾個魔術方法
__setattr__(self,name,value):如果要給 name 賦值,就調用這個方法。 __getattr__(self,name):如果 name 被訪問,同時它不存在的時候,此方法被調用。 __getattribute__(self,name):當 name被訪問時自動被調用(注意:這個僅能用于新式類),無論 name 是否存在,都要被調用。 __delattr__(self,name):如果要刪除 name,這個方法就被調用。 >>> class A(object): ... def __getattr__(self, name): ... print "You use getattr" ... def __setattr__(self, name, value): ... print "You use setattr" ... self.__dict__[name] = value # a.x,按照本節開頭的例子,是要報錯的。但是,由于在這里使用了__getattr__(self, name) 方法,當發現 x 不存在于對象的__dict__中的時候,就調用了__getattr__,即所謂“攔截成員”。 >>> a = A() >>> a.x You use getattr # 給對象的屬性賦值時候,調用了__setattr__(self, name, value)方法,這個方法中有一句 self.__dict__[name] = value,通過這個語句,就將屬性和數據保存到了對象的__dict__中 >>> a.x = 7 You use setattr # 測試__getattribute__(self,name) >>> class B(object): ... def __getattribute__(self, name): ... print "you are useing getattribute" ... return object.__getattribute__(self, name) # 返回的內容用的是 return object.__getattribute__(self, name),而沒有使用 return self.__dict__[name]。因為如果用這樣的方式,就是訪問 self.__dict__,只要訪問這個屬性,就要調用`getattribute``,這樣就導致了無限遞歸 # 訪問不存在的成員,可以看到,已經被__getattribute__攔截了,雖然最后還是要報錯的。 >>> b = B() >>> b.y you are useing getattribute Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 4, in __getattribute__ AttributeError: 'B' object has no attribute 'y'
Property函數
porperty可以作為裝飾器使用把方法標記為特性
class Vector(object): def __init__(self, x, y): # 使用兩個前導下劃線,把屬性標記為私有 self.__x = float(x) self.__y = float(y) # porperty裝飾器把讀值方法標記為特性 @property def x(self): return self.__x @property def y(self): return self.__y vector = Vector(3,4) print(vector.x, vector.y)
使用property可以將函數封裝為屬性
class Rectangle(object): """ the width and length of Rectangle """ def __init__(self): self.width = 0 self.length = 0 def setSize(self, size): self.width, self.length = size def getSize(self): return self.width, self.length if __name__ == "__main__": r = Rectangle() r.width = 3 r.length = 4 print r.getSize() # (3,4) r.setSize( (30, 40) ) print r.width # 30 print r.length # 40
這段代碼可以正常運行,但是屬性的調用方式可以改進,如下:
class Rectangle(object): """ the width and length of Rectangle """ def __init__(self): self.width = 0 self.length = 0 def setSize(self, size): self.width, self.length = size def getSize(self): return self.width, self.length # 使用property方法將函數封裝為屬性,更優雅 size = property(getSize, setSize) if __name__ == "__main__": r = Rectangle() r.width = 3 r.length = 4 print r.size # (30, 40) r.size = 30, 40 print r.width # 30 print r.length # 40
使用魔術方法實現:
class NewRectangle(object): def __init__(self): self.width = 0 self.length = 0 def __setattr__(self, name, value): if name == 'size': self.width, self, length = value else: self.__dict__[name] = value def __getattr__(self, name): if name == 'size': return self.width, self.length else: raise AttrubuteErrir if __name__ == "__main__": r = Rectangle() r.width = 3 r.length = 4 print r.size # (30, 40) r.size = 30, 40 print r.width # 30 print r.length # 40
屬性的獲取順序
最后我們來看看熟悉的獲得順序:通過實例獲取其屬性,如果在__dict__中有相應的屬性,就直接返回其結果;如果沒有,會到類屬性中找。
看下面一個例子:
class A(object): author = "qiwsir" def __getattr__(self, name): if name != "author": return "from starter to master." if __name__ == "__main__": a = A() print a.author # qiwsir print a.lang # from starter to master.
當 a = A() 后,并沒有為實例建立任何屬性,或者說實例的__dict__是空的。但是如果要查看 a.author,因為實例的屬性中沒有,所以就去類屬性中找,發現果然有,于是返回其值 “qiwsir”。但是,在找 a.lang的時候,不僅實例屬性中沒有,類屬性中也沒有,于是就調用了__getattr__()方法。在上面的類中,有這個方法,如果沒有__getattr__()方法呢?如果沒有定義這個方法,就會引發 AttributeError,這在前面已經看到了。
以上是“Python如何獲取對象屬性”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。