鍍金池/ 教程/ Python/ 特殊方法 (1)
標(biāo)準(zhǔn)庫 (4)
如何成為 Python 高手
標(biāo)準(zhǔn)庫 (6)
標(biāo)準(zhǔn)庫 (3)
類(2)
Pandas 使用 (2)
xml
用 tornado 做網(wǎng)站 (5)
文件(1)
練習(xí)
列表(3)
從小工到專家
除法
錯誤和異常 (2)
函數(shù)(1)
用 tornado 做網(wǎng)站 (7)
為做網(wǎng)站而準(zhǔn)備
函數(shù)練習(xí)
標(biāo)準(zhǔn)庫 (8)
Pandas 使用 (1)
回顧 list 和 str
字典(1)
用 tornado 做網(wǎng)站 (3)
字符串(1)
函數(shù)(2)
寫一個簡單的程序
將數(shù)據(jù)存入文件
語句(5)
SQLite 數(shù)據(jù)庫
集成開發(fā)環(huán)境(IDE)
集合(1)
類(1)
用 tornado 做網(wǎng)站 (6)
用 tornado 做網(wǎng)站 (2)
自省
語句(4)
錯誤和異常 (1)
用 tornado 做網(wǎng)站 (4)
集合(2)
列表(1)
標(biāo)準(zhǔn)庫 (1)
生成器
mysql 數(shù)據(jù)庫 (1)
第三方庫
實(shí)戰(zhàn)
運(yùn)算符
類(3)
字典(2)
語句(1)
數(shù)和四則運(yùn)算
語句(2)
文件(2)
MySQL 數(shù)據(jù)庫 (2)
電子表格
迭代器
mongodb 數(shù)據(jù)庫 (1)
特殊方法 (2)
特殊方法 (1)
字符編碼
編寫模塊
用 tornado 做網(wǎng)站 (1)
標(biāo)準(zhǔn)庫 (5)
函數(shù)(4)
類(5)
字符串(2)
關(guān)于 Python 的故事
函數(shù)(3)
字符串(4)
處理股票數(shù)據(jù)
常用數(shù)學(xué)函數(shù)和運(yùn)算優(yōu)先級
字符串(3)
為計(jì)算做準(zhǔn)備
多態(tài)和封裝
類(4)
迭代
語句(3)
錯誤和異常 (3)
分析 Hello
Python 安裝
標(biāo)準(zhǔn)庫 (2)
列表(2)
元組

特殊方法 (1)

探究更多的類屬性,在一些初學(xué)者的教程中,一般很少見。我之所以要在這里也將這部分奉獻(xiàn)出來,就是因?yàn)楸窘坛淌恰癋rom Beginner to Master”。當(dāng)然,不是學(xué)習(xí)了類的更多屬性就能達(dá)到 Master 水平,但是這是通往 Master 的一步,雖然在初級應(yīng)用中,本節(jié)乃至于后面關(guān)于類的屬性用的不很多,但是,這一步邁出去,你就會在實(shí)踐中有一個印象,以后需要用到了,知道有這一步,會對項(xiàng)目有幫助的。俗話說“藝不壓身”。

__dict__

前面已經(jīng)學(xué)習(xí)過有關(guān)類屬性和實(shí)例屬性的內(nèi)容,并且做了區(qū)分,如果忘記了可以回頭參閱《類(3)》中的“類屬性和實(shí)例屬性”部分。有一個結(jié)論,是一定要熟悉的,那就是可以通過 object.attribute 的方式訪問對象的屬性。

如果接著那部分內(nèi)容,讀者是否思考過一個問題:類或者實(shí)例屬性,在 Python 中是怎么存儲的?或者為什么修改或者增加、刪除屬性,我們能不能控制這些屬性?

>>> class A(object):
...     pass
...

>>> a = A()
>>> dir(a)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
>>> dir(A)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']

dir() 來查看一下,發(fā)現(xiàn)不管是類還是實(shí)例,都有很多屬性,這在前面已經(jīng)反復(fù)出現(xiàn),有點(diǎn)見怪不怪了。不過,這里我們要看一個屬性:__dict__,因?yàn)樗且粋€保存秘密的東西:對象的屬性。

>>> class Spring(object):
...     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})

為了便于觀察,我將上面的顯示結(jié)果進(jìn)行了換行,每個鍵值對一行。

對于類 Spring 的__dict__屬性,可以發(fā)現(xiàn),有一個鍵'season',這就是這個類的屬性;其值就是類屬性的數(shù)據(jù)。

>>> Spring.__dict__['season']
'the spring of class'
>>> Spring.season
'the spring of class'

用這兩種方式都能得到類屬性的值?;蛘哒f Spring.__dict__['season'] 就是訪問類屬性。下面將這個類實(shí)例化,再看看它的實(shí)例屬性:

>>> s = Spring()
>>> s.__dict__
{}

實(shí)例屬性的__dict__是空的。有點(diǎn)奇怪?不奇怪,接著看:

>>> s.season
'the spring of class'

這個其實(shí)是指向了類屬性中的 Spring.season,至此,我們其實(shí)還沒有建立任何實(shí)例屬性呢。下面就建立一個實(shí)例屬性:

>>> s.season = "the spring of instance"
>>> s.__dict__
{'season': 'the spring of instance'}

這樣,實(shí)例屬性里面就不空了。這時(shí)候建立的實(shí)例屬性和上面的那個 s.season 只不過重名,并且把它“遮蓋”了。這句好是不是熟悉?因?yàn)樵谥v述“實(shí)例屬性”和“類屬性”的時(shí)候就提到了。現(xiàn)在讀者肯定理解更深入了。

>>> s.__dict__['season']
'the spring of instance'
>>> s.season
'the spring of instance'

此時(shí),那個類屬性如何?我們看看:

>>> 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})
>>> Spring.season
'the spring of class'

Spring 的類屬性沒有受到實(shí)例屬性的影響。

按照前面的講述類屬性和實(shí)例熟悉的操作,如果這時(shí)候?qū)⑶懊娴膶?shí)例屬性刪除,會不會回到實(shí)例屬性s.__dict__為空呢?

>>> del s.season
>>> s.__dict__
{}
>>> s.season
'the spring of class'

果然打回原形。

當(dāng)然,你可以定義其它名稱的實(shí)例屬性,它一樣被存儲到__dict__屬性里面:

>>> s.lang = "python"
>>> s.__dict__
{'lang': 'python'}
>>> s.__dict__['lang']
'python'

誠然,這樣做僅僅是更改了實(shí)例的__dict__內(nèi)容,對 Spring.__dict__無任何影響,也就是說通過 Spring.lang 或者 Spring.__dict__['lang'] 是得不到上述結(jié)果的。

>>> Spring.lang
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'Spring' has no attribute 'lang'

>>> Spring.__dict__['lang']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'lang'

那么,如果這樣操作,會怎樣呢?

>>> 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__被更改了,類屬性中增加了一個'flower'屬性。但是,實(shí)例的__dict__中如何?

>>> s.__dict__
{'lang': 'python'}

沒有被修改。我也是這么想的,哈哈。你此前這這么覺得嗎?然而,還能這樣:

>>> s.flower
'peach'

這個讀者是否能解釋?其實(shí)又回到了前面第一個出現(xiàn) s.season 上面了。

通過上面探討,是不是基本理解了實(shí)例和類的__dict__,并且也看到了屬性的變化特點(diǎn)。特別是,這些屬性都是可以動態(tài)變化的,就是你可以隨時(shí)修改和增刪。

屬性如此,方法呢?下面就看看方法(類中的函數(shù))。

>>> class Spring(object):
...     def tree(self, x):
...         self.x = x
...         return self.x
... 
>>> 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>

結(jié)果跟前面討論屬性差不多,方法 tree 也在__dict__里面呢。

>>> t = Spring()
>>> t.__dict__
{}

又跟前面一樣。雖然建立了實(shí)例,但是在實(shí)例的__dict__中沒有方法。接下來,執(zhí)行:

>>> t.tree("xiangzhangshu")
'xiangzhangshu'

類(3)中有一部分內(nèi)容闡述“數(shù)據(jù)流轉(zhuǎn)”,其中有一張圖,其中非常明確顯示出,當(dāng)用上面方式執(zhí)行方法的時(shí)候,實(shí)例 tself 建立了對應(yīng)關(guān)系,兩者是一個外一個內(nèi)。在方法中 self.x = x,將 x 的值給了 self.x,也就是實(shí)例應(yīng)該擁有了這么一個屬性。

>>> t.__dict__
{'x': 'xiangzhangshu'}

果然如此。這也印證了實(shí)例 tself 的關(guān)系,即實(shí)例方法(t.tree('xiangzhangshu'))的第一個參數(shù)(self,但沒有寫出來)綁定實(shí)例 t,透過 self.x 來設(shè)定值,即給 t.__dict__添加屬性值。

換一個角度:

>>> class Spring(object):
...     def tree(self, x):
...         return x
...

這回方法中沒有將 x 賦值給 self 的屬性,而是直接 return,結(jié)果是:

>>> s = Spring()
>>> s.tree("liushu")
'liushu'
>>> s.__dict__
{}

是不是理解更深入了?

現(xiàn)在需要對 Python 中一個觀點(diǎn):“一切皆對象”,再深入領(lǐng)悟。以上不管是類還是的實(shí)例的屬性和方法,都是符合 object.attribute 格式,并且屬性類似。

當(dāng)你看到這里的時(shí)候,要么明白了類和實(shí)例的__dict__的特點(diǎn),要么就糊涂了。糊涂也不要緊,再將上面的重復(fù)一遍,特別是自己要敲一敲有關(guān)代碼。(建議一個最好的方法:用兩個顯示器,一個顯示器看本教程,另外一個顯示器敲代碼。事半功倍的效果。)

需要說明,我們對__dict__的探討還留有一個尾巴:屬性搜索路徑。這個留在后面講述。

不管是類還是實(shí)例,其屬性都能隨意增加。這點(diǎn)在有時(shí)候不是一件好事情,或許在某些時(shí)候你不希望別人增加屬性。有辦法嗎?當(dāng)然有,請繼續(xù)學(xué)習(xí)。

__slots__

首先聲明,__slots__能夠限制屬性的定義,但是這不是它存在終極目標(biāo),它存在的終極目標(biāo)更應(yīng)該是一個在編程中非常重要的方面:優(yōu)化內(nèi)存使用。

>>> class Spring(object):
...     __slots__ = ("tree", "flower")
... 
>>> dir(Spring)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'flower', 'tree']

仔細(xì)看看 dir() 的結(jié)果,還有__dict__屬性嗎?沒有了,的確沒有了。也就是說__slots____dict__擠出去了,它進(jìn)入了類的屬性。

>>> Spring.__slots__
('tree', 'flower')

這里可以看出,類 Spring 有且僅有兩個屬性。

>>> t = Spring()
>>> t.__slots__
('tree', 'flower')

實(shí)例化之后,實(shí)例的__slots__與類的完全一樣,這跟前面的__dict__大不一樣了。

>>> Spring.tree = "liushu"

通過類,先賦予一個屬性值。然后,檢驗(yàn)一下實(shí)例能否修改這個屬性:

>>> t.tree = "guangyulan"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Spring' object attribute 'tree' is read-only

看來,我們的意圖不能達(dá)成,報(bào)錯信息中顯示,tree 這個屬性是只讀的,不能修改了。

>>> t.tree
'liushu'

因?yàn)榍懊嬉呀?jīng)通過類給這個屬性賦值了。不能用實(shí)例屬性來修改。只能:

>>> Spring.tree = "guangyulan"
>>> t.tree
'guangyulan'

用類屬性修改。但是對于沒有用類屬性賦值的,可以通過實(shí)例屬性:

>>> t.flower = "haitanghua"
>>> t.flower
'haitanghua'

但此時(shí):

>>> Spring.flower
<member 'flower' of 'Spring' objects>

實(shí)例屬性的值并沒有傳回到類屬性,你也可以理解為新建立了一個同名的實(shí)例屬性。如果再給類屬性賦值,那么就會這樣了:

>>> Spring.flower = "ziteng"
>>> t.flower
'ziteng'

當(dāng)然,此時(shí)在給 t.flower 重新賦值,就會爆出跟前面一樣的錯誤了。

>>> t.water = "green"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Spring' object has no attribute 'water'

這里試圖給實(shí)例新增一個屬性,也失敗了。

看來__slots__已經(jīng)把實(shí)例屬性牢牢地管控了起來,但更本質(zhì)是的是優(yōu)化了內(nèi)存。誠然,這種優(yōu)化會在大量的實(shí)例時(shí)候顯出效果。


總目錄   |   上節(jié):多態(tài)和封裝   |   下節(jié):特殊方法(2)

如果你認(rèn)為有必要打賞我,請通過支付寶:qiwsir@126.com,不勝感激。

上一篇:錯誤和異常 (3)下一篇:字符串(2)