__dict__
- Python 中,一切皆對象
- 不管是類還是實例的屬性和方法,都符合
object.attribute
格式。并且屬性類型
class A(object):
pass
a = A()
dir(a)
輸出:
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__','__eq__','__format__','__ge__','__getattribute__','__gt__','__hash__','__init__','__init_subclass__','__le__','__lt__', '__module__','__ne__','__new__', '__reduce__', '__reduce_ex__','__repr__','__setattr__', '__sizeof__', '__str__', '__subclasshook__','__weakref__']
class Spring(object):
season = 'the spring of class'
Spring.__dict__
輸出:
mappingproxy({'__dict__': <attribute '__dict__' of 'Spring' objects>,
'__doc__': None,
'__module__': '__main__',
'__weakref__': <attribute '__weakref__' of 'Spring' objects>,
'season': 'the spring of class'})
#訪問的是類屬性
print(Spring.__dict__['season'])
print(Spring.season)
輸出:
'the spring of class'
#訪問的是實例屬性,因為class中沒有定義實例屬性所以打印為空
s = Spring()
s.__dict__
輸出:
{}
s.season #訪問的類屬性
輸出:
'the spring of class'
#為class 的實例 中添加一個 season 屬性
s.season = "instance";
s.__dict__
輸出:
{'season': 'instance'}
#當類屬性和實例屬性重名時,實例屬性會覆蓋掉類屬性
s.__dict__['season']
輸出:
'instance'
#但是類屬性不會變
Spring.__dict__['season']
輸出:
'the spring of class'
Spring.season
輸出:
'the spring of class'
#刪除實例屬性后,就回到了實例化類的時候的狀態,沒有實例屬性,只有類屬性
del s.season
s.__dict__
輸出:
{}
s.season #類屬性
輸出:
'the spring of class'
#屬性和方法是同樣的
class Spring_def(object):
def tree(self,x):
self.x= x
return self.x
Spring_def.__dict__
輸出:
mappingproxy({'__dict__': <attribute '__dict__' of 'Spring_def' objects>,
'__doc__': None,
'__module__': '__main__',
'__weakref__': <attribute '__weakref__' of 'Spring_def' objects>,
'tree': <function __main__.Spring_def.tree>})
Spring_def.__dict__['tree']
輸出:
<function __main__.Spring_def.tree>
t = Spring_def()
t.__dict__
輸出:
{}
t.tree('value')
t.__dict__
輸出:
{}
class Spring_no(object):
def tree(self,x):
return x
ts = Spring_no()
ts.tree('No arg')
ts.__dict__
輸出:
{}
Spring_no.__dict__
輸出:
mappingproxy({'__dict__': <attribute '__dict__' of 'Spring_no' objects>,
'__doc__': None,
'__module__': '__main__',
'__weakref__': <attribute '__weakref__' of 'Spring_no' objects>,
'tree': <function __main__.Spring_no.tree>})
__slots__
- 能夠限制屬性的定義
- 優化內存使用
- 把
__dict__
替換成__slots__
- 用類給一個屬性賦值后,實例就不能給這個屬性賦值了。
- 也不能新增實例屬性,這樣就把屬性管控了起來。內存得到優化
- 大量實例的話會顯現出效果
class Spring_s(object):
__slots__ = ("tree","flows")
dir(Spring_s)
輸出:
['__class__','__delattr__','__dir__','__doc__','__eq__', '__format__','__ge__','__getattribute__', '__gt__','__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__','__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'flows', 'tree']
Spring_s.__dict__
輸出:
mappingproxy({'__doc__': None,
'__module__': '__main__',
'__slots__': ('tree', 'flows'),
'flows': <member 'flows' of 'Spring_s' objects>,
'tree': <member 'tree' of 'Spring_s' objects>})
Spring_s.__slots__
輸出:
('tree', 'flows')
ts = Spring_s()
ts.__slots__
輸出:
('tree', 'flows')
#使用__slots__后就沒有__dict__ 屬性了
ts.__dict__
輸出:
#錯誤
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-40-f3db88226dd5> in <module>()
1 #使用__slots__后就沒有__dict__ 屬性了
----> 2 ts.__dict__
AttributeError: 'Spring_s' object has no attribute '__dict__'
Spring_s.tree = "liushu"
Spring_s.tree
輸出:
'liushu'
# 報錯中顯示 實例屬性 tree 只是制度的,不能修改
ts.tree = "yangshu"
輸出:
#錯誤
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-42-d6b9cb191c20> in <module>()
1 # 報錯中顯示 實例屬性 tree 只是制度的,不能修改
----> 2 ts.tree = "yangshu"
AttributeError: 'Spring_s' object attribute 'tree' is read-only
ts.tree
輸出:
'liushu'
- 前面已經通過類給屬性賦值了,就不能用實例屬性來修改。
- 當使用實例給屬性賦值后,還是可以再用實例來給屬性賦值,但是當這個屬性被類賦值后,就不能再用實例給屬性賦值了,只能用類來給屬性賦值.
#第一次實例賦值
ts.flows = "meigui"
ts.flows
輸出:
'meigui'
#第二次實例賦值
ts.flows = "yueji"
ts.flows
輸出:
'yueji'
#使用類賦值
Spring_s.flows = "shuixianhua"
Spring_s.flows
輸出:
'shuixianhua'
#實例的屬性的值會變成類屬性的值
ts.flows
輸出:
'shuixianhua'
#再用實例給屬性賦值就會報只讀錯誤
ts.flows = "qianniuhua"
輸出:
#錯誤
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-48-726c06affe50> in <module>()
1 #再用實例給屬性賦值就會報只讀錯誤
----> 2 ts.flows = "qianniuhua"
AttributeError: 'Spring_s' object attribute 'flows' is read-only
Spring_s.flows = "baihe"
Spring_s.flows
輸出:
'baihe'
ts.flows
輸出:
'baihe'
- 實例中添加屬性
# 新增實例失敗
ts.water = "green"
輸出:
#錯誤
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-52-cde7efd2f5b8> in <module>()
----> 1 ts.water = "green"
AttributeError: 'Spring_s' object has no attribute 'water'
__getattr__
、__setattr__
和其他類似方法
-
__setattr__(self,name,value):
如果要給name賦值,就調用這個方法 -
__getattr__(self,name):
如果name被訪問,同事它不存在,此方法被調用 -
__getattribute__(self,name):
當name被訪問時自動被調用(注意:這個僅能用于新式類),無論name是否存在,都要被調用 -
__delattr__(self,name):
如果要刪除name,這個方法就被調用。
class B(object):
pass
b = B()
b.x
輸出:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-42-f8f5c58b62c7> in <module>()
2 pass
3 a = A()
----> 4 a.x
AttributeError: 'A' object has no attribute 'x'
x 不是勢力的成員(屬性和方法)所以報錯。也就是說,如果訪問 b.x ,它不存在,那么就要轉向到某個操作 ,我們把這種情況叫做“攔截”。
class C(object):
def __getattr__(self,name):
print('you use getattr')
def __setattr__(self,name,value):
print('you use setattr')
self.__dict__[name] = value
類C是新式類,出來2個方法,沒有別的屬性
c = C()
c.x
輸出:
you use getattr
本來按照開頭class B
是要報錯的。但是由于這里使用了 __getattr__(self,name)
當發現x不存在與對象__dict__
時,就會調用__getattr
“攔截成員” 。
c.x = 7
輸出:
you use setattr
給對象的屬性賦值是,調用了__setattr__(self,name,value)
方法,這個方法有一句 self.__dict__[name] = value
通過這個語句,就將屬性和數據保存到了對象的 __dict__
中。如果再調用這個屬性:
c.x # x 已經存在于對象的 __dict__中
輸出:
7
使用__getattribute__(self,name):
,只要訪問屬性就會調用它
class getAt(object):
def __getattribute__(self,name):
print('使用了getattribute')
return object.__getattribute__(self,name)
at = getAt()
at.y
輸出:
使用了getattribute
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-54-6e81b53b32de> in <module>()
1 at = getAt()
----> 2 at.y
<ipython-input-53-a6779a4b213a> in __getattribute__(self, name)
2 def __getattribute__(self,name):
3 print('使用了getattribute')
----> 4 return object.__getattribute__(self,name)
AttributeError: 'getAt' object has no attribute 'y'
- 訪問一個不存在的屬性,雖然攔截了但還是報錯了。
- 但是只要賦值后就可以訪問了,因為這樣就意味著做個屬性已經存在于
__dict__
中了。雖然依然被攔截,但是會把結果返回。
at.y = 9
at.y
輸出:
使用了getattribute
9
注意:這里沒有
return self.__dict__[name]
,因為如果用這樣的方法訪問self.__dict__
只要訪問這個方法,就會去調用__getattribute__
,這樣就會無限遞歸下去,造成死循環。
示例:
''' study __getattr__ and __setattr__'''
class Rectangle(object):
''' the width and length of Rectangle'''
def __init__(self):
self.width = 0
self.height = 0
def setSize(self,size):
self.width,self.height = size
def getSize(self):
return self.width,self.height
if __name__ == "__main__":
r = Rectangle()
r.width = 3
r.height = 4
print (r.getSize())
r.setSize((30,40))
print(r.width)
print(r.height)
輸出:
(3, 4)
30
40
長寬賦值的時候必須是一個元組,里面包含長寬。改進。。。
class Rectangle_2(object):
''' 使用property改進調用方法傳參'''
def __init__(self):
self.width = 0
self.height = 0
def setSize(self,size):
self.width,self.height = size
def getSize(self):
return self.width,self.height
#---------新加---------#
size = property(getSize,setSize)
#---------結束---------#
if __name__ == "__main__":
r = Rectangle_2()
r.width = 3
r.length = 4
print(r.size)
r.size = 30, 40
print (r.width)
print (r.length)
輸出:
(3, 0)
30
4
雖然方法調用就像屬性一樣,但是沒有用上特殊方法。繼續改進。。。
class Rectangle_3(object):
'''使用特殊方法'''
def __init__(self):
self.width = 0
self.height = 0
def __setattr__(self, name, value):
if name == "size":
self.width, self.height = value
else:
self.__dict__[name] = value
def __getattr__(self, name):
if name == "size":
return self.width,self.height
else:
return AttributeError
if __name__ == "__main__":
nr = Rectangle_3()
nr.width = 3
nr.height = 4
print(nr.size)
nr.size = 30,40
print(nr.width)
print(nr.height)
輸出:
(3, 4)
30
40
獲得屬性順序
- 通過實例獲取其屬性,如果在
__dict__
中有,就直接返回其結果,如果沒有,就會到類屬性中找。
class search(object):
author = 'Python'
def __getattr__(self , name):
if name != 'author':
return '查詢的不是author屬性'
if __name__ == '__main__':
a = search()
print(a.author)
print(a.none)
print(a.__dict__)
輸出:
Python
查詢的不是author屬性
{}
- 初始化后 a 沒有建立任何實例屬性。實例屬性
__dict__
是空,沒有屬性值,a.author
則有值,因為是類屬性,而實例中又沒有,所以就去類屬性中去找,所以就返回了'Python'
- 當a.none 的時候不僅實例中沒有,類中也沒有,所以就調用了
__getattr__
方法。如果不寫__getattr__
方法就會報錯了。 - 這就是 通過實例查找特性的順序
參考:《跟著老齊學Python:從入門到精通》 作者:齊偉 電子工業出版社