今天,談談python中的資料模型,當然你可以不瞭解這些東西,照樣可以寫出漂亮的python代碼,但是“知其然知其所以然”是我的作風,總是不明白python的一些機制,心裡很不爽。結合python的doc和一篇文章,差不多明白了python的哲理。
我覺得有必要將python中的文檔的一些重要語句拿出來。
Objects are Python’s abstraction for data. All data in a Python program is represented by objects or by relations between objects.
Every object has an identity, a type and a value.
為什麼我這裡要強調這裡的對象,有對象以為著有一些屬性(函數也罷,變數也罷都是屬性)與之關聯,你操作一個對象,其實就是在和這些屬性打交道。
在這裡,我集中在python中的類,去弄明白python中的類究竟是怎麼完成其功能的.
A class has a namespace implemented by a dictionary object. Class attribute references are translated to lookups in this dictionary.
這句話很重要,一個類有其自己的namespace(命名空間),而且這個命名空間的實現本質上是一個字典。換句話說,你對一個類的操作其實就是在對這個“特殊”的字典進行操作,那麼怎麼去區分類的執行個體和類呢,其實在python中,類的執行個體也有一個字典,去記錄該執行個體的屬性。
view plaincopy to clipboardprint?
- class T(object):
- age = 21
-
- t = T()
- t.name = "ilovebaiyang"
-
- print "dir(T):", dir(T)
- print "dir(t):", dir(t)
這個列印的結果是:
view plaincopy to clipboardprint?
- dir(T): ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age']
-
- dir(t): ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name']
其中有些屬性是從object上繼承來的,比如__doc__, __setattr__,__dict__等等,其中值得注意的是自訂屬性,age和name,他們分別存在於類T中和類T的執行個體t中,那麼為什麼在dir(t)中出現了age的屬性呢,原因是從它的類上複製過來的,而且如果我們列印他們的id,其實他們的id是一樣的。在這裡,我們出現了一個疑問,前面提到的命名空間的字典就是dir()函數產生的字典麼,看上去貌似是,因為它的確記錄了該對象的所有屬性,其實這個是錯誤的,原因我們就要回到dir函數上了,它的作用是羅列出當前對象的所有屬性,包括父類的。其實,前面提到的命名空間是的__dict__成員,所有與該對象對應的屬性全部放在這個字典裡。
我們列印出來看看
view plaincopy to clipboardprint?
- T.__dict__: {'__dict__': , '__module__': '__main__', '__weakref__': , 'age': 21, '__doc__': None}
-
- t.__dict__: {'name': 'ilovebaiyang'}
這一下才會和我們想象的一樣,name屬性只存在執行個體中。
接下來是屬性的尋找:
如果我們要列印t.age,怎麼搜尋
- 1.先尋找該執行個體的__dict__,如果有,返回,否則進行第二步
- 2.尋找類的__dict__,如果有返回,沒有的話,繼續尋找該類的父類,直到基類,如果還沒有,拋出異常。
當然,其實python中的尋找還做了很多其他事,特別是一些隱含的調用,如果我們重寫這些隱含的調用,就可以寫出符合我們的資料。
看下面的例子:
view plaincopy to clipboardprint?
- class T(object):
- def __setattr__(self, name, value):
- print "__setattr__ called "
- object.__setattr__(self, name, value)
-
- def __getattr__(self, name):
- print "__getattr__ called "
-
- def __getattribute__(self, name):
- print "__getattribute__ called"
- return object.__getattribute__(self, name)
-
- t = T()
- t.name = "baiyang"
- print "t", t.name
- t.baiyang
結果:
- __setattr__ called
- t __getattribute__ called
- baiyang
- __getattribute__ called
- __getattr__ called
分析以上的結果,當t.name被執行時,先調用了__setattr__函數;當print “t”, t.name時,調用了__getattribute__函數;當t.baiyang時,調用了__getattr__函數,因為t沒有baiyang這個屬性,故調用這個函數。
其實簡單的想一下,會明白,我們在操作namespace這個字典時,我們都會隱式的調用相應的函數,這些函數可以用來進行資料的驗證。
總結一下,在操作類的執行個體的屬性時,我們需要關注一下函數:
object.__setattr__(self, name, value)
object.__getattr__(self, name)
object.__delattr__(self, name)
object.__getattribute__(self, name)
其中你對任何對象的屬性訪問時,都會隱式的調用__getattribute__方法,比如你調用t.__dict__,其實你執行了t.__getattribute__(“__dict__”)函數。
神奇的python,把字典用的如此靈活,由此可以看出字典在python的地位是何其的重要。
在這裡,我們或許想到了怎麼不去隱式的調用這些函數,對,我們可以直接去操作類或者類執行個體的__dict__。看看下面的例子:
view plaincopy to clipboardprint?
- class T(object):
- age = 21
- def __setattr__(self, name, value):
- print "__setattr__ called "
- object.__setattr__(self, name, value)
-
- def __getattr__(self, name):
- print "__getattr__ called "
-
- def __getattribute__(self, name):
- print "__getattribute__ called"
- return object.__getattribute__(self, name)
-
- t = T()
- t.__dict__["name"] = "baiyang"
- print "t.__dict__: ", t.__dict__["name"]
- print "t.age: ", t.age
- print "t.__dict__['age']: ", t.__dict__["age"]
結果
view plaincopy to clipboardprint?
- __getattribute__ called
- t.__dict__: __getattribute__ called
- baiyang
- t.age: __getattribute__ called
- 21
- t.__dict__['age']: __getattribute__ called
-
- Traceback (most recent call last):
- File "C:\Users\Administrator\Desktop\lab\SogouW\Freq\test.py", line 18, in
- print "t.__dict__['age']: ", t.__dict__["age"]
- KeyError: 'age'
我們來分析一下:
1.t__dict__["name"] = “baiyang”其中調用了__getattribute__函數,因為__dict__本身也是一個屬性的,所以它必須會執行的;但是你會發現此時__setattr__並沒有執行,達到了我們想要的效果;
2.print “t.age: “, t.age
print “t.__dict__['age']: “, t.__dict__["age"]這才是重點,第二條語句拋出了KeyError的異常,再一次證明了age只是存在於類的namespace中。
最後,我們與python中的descriptor進行比較:
可以看這篇文章,我就不細講了。
主要一點就是descriptor對應的函數是
object.__get__(self, instance, owner)
object.__set__(self, instance, value)
object.__delete__(self, instance)
可以看出少了attr這個詞,也以為著我們必須將他們區分看。descriptor是對類的執行個體進行的操作,內建函數property()就是通過這個方式實現的。看了此文,是不是對python的工作機制更加瞭解了呢?
好了,在這裡提幾個問吧,你可以去網上搜到答案
- 如何建立immutable的資料,我之前的一篇文章已經給出了答案。
- 如何建立singleton模式的資料
如果你明白了上面的文章,你就會明白,不論你想寫出什麼樣的資料,只要去重寫python中一些內在的函數即可。
-----------------打造高品質的文章 更多關注 把酒泯恩仇---------------
為了打造高品質的文章,請 推薦 一個吧。。。。謝謝了,我會寫更多的好文章的。
請關注sina微博:http://weibo.com/baiyang26
把酒泯恩仇官方部落格:http://www.ibaiyang.org 【推薦用google reader訂閱】
把酒泯恩仇官方豆瓣:http://www.douban.com/people/baiyang26/