深刻理解python對象及屬性

類屬性和實例屬性
首先來看看類屬性和類實例的屬性在python中如何存儲,經過__dir__方法來查看對象的屬性python

>>> class Test(object):
pass
>>> test = Test(http://www.my516.com)
# 查看類屬性
>>> 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__']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
咱們主要看一個屬性__dict__,由於 __dict__保存的對象的屬性,看下面一個例子spring

>>> class Spring(object):
... season = "the spring of class"
...ssh

# 查看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})ide

# 經過兩種方法訪問類屬性
>>> Spring.__dict__['season']
'the spring of class'
>>> Spring.season
'the spring of class'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
發現__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})orm

# 若是將實例屬性刪除,又會調用類屬性
>>> 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'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
下面看看類中包含方法,__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__
{}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
須要理解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'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
若是使用的當,__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'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
使用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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
這段代碼能夠正常運行,可是屬性的調用方式能夠改進,以下:

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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
使用魔術方法實現:

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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
屬性的獲取順序
最後咱們來看看熟悉的得到順序:經過實例獲取其屬性,若是在__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 # qiwsirprint a.lang # from starter to master.12345678910當 a = A() 後,並無爲實例創建任何屬性,或者說實例的__dict__是空的。可是若是要查看 a.author,由於實例的屬性中沒有,因此就去類屬性中找,發現果真有,因而返回其值 「qiwsir」。可是,在找 a.lang的時候,不只實例屬性中沒有,類屬性中也沒有,因而就調用了__getattr__()方法。在上面的類中,有這個方法,若是沒有__getattr__()方法呢?若是沒有定義這個方法,就會引起 AttributeError,這在前面已經看到了。--------------------- 

相關文章
相關標籤/搜索