一般說來,特殊的方法都被用來模仿某個行為。例如,如果你想要為你的類使用x[key]這樣的索引操作(就像列表和元組一樣),那么你只需要實現__getitem__()方法就可以了。想一下,Python就是對list類這樣做的!
下面這個表中列出了一些有用的特殊方法。如果你想要知道所有的特殊方法,你可以在《Python參考手冊》中找到一個龐大的列表。



















exec和eval語句
exec語句用來執行儲存在字符串或文件中的Python語句。例如,我們可以在運行時生成一個包含Python代碼的字符串,然后使用exec語句執行這些語句。
下面是一個簡單的例子。


eval語句用來計算存儲在字符串中的有效Python表達式。下面是一個簡單的例子。


assert語句
assert 語句用來聲明某個條件是真的。例如,如果你非常確信某個你使用的列表中至少有一個元素,而你想要檢驗這一點,並且在它非真的時候引發一個錯誤,那么 assert語句是應用在這種情形下的理想語句。當assert語句失敗的時候,會引發一個AssertionError。









repr函數用來取得對象的規范字符串表示。反引號(也稱轉換符)可以完成相同的功能。注意,在大多數時候有eval(repr(object)) == object。






基本上,repr函數和反引號用來獲取對象的可打印的表示形式。你可以通過定義類的__repr__方法來控制你的對象在被repr函數調用的時候返回的內容。
類和實例變量
有兩種類型的域 —— 類的變量和對象的變量,它們根據是類還是對象擁有這個變量而區分。
類的變量 由一個類的所有對象(實例)共享使用。只有一個類變量的拷貝,所以當某個對象對類的變量做了改動的時候,這個改動會反映到所有其他的實例上。
對象的變量 由類的每個對象/實例擁有。因此每個對象有自己對這個域的一份拷貝,即它們不是共享的,在同一個類的不同實例中,雖然對象的變量有相同的名稱,但是是互不相關的。通過一個例子會使這個易於理解。
































































這是一個很長的例子,但是它有助於說明類與對象的變量的本質。這里,population屬於Person類,因此是一個類的變量。name變量屬於對象(它使用self賦值)因此是對象的變量。
觀察可以發現__init__方法用一個名字來初始化Person實例。在這個方法中,我們讓population增加1,這是因為我們增加了一個人。同樣可以發現,self.name的值根據每個對象指定,這表明了它作為對象的變量的本質。
記住,你只能使用self變量來參考同一個對象的變量和方法。這被稱為 屬性參考 。
在這個程序中,我們還看到docstring對於類和方法同樣有用。我們可以在運行時使用Person.__doc__和Person.sayHi.__doc__來分別訪問類與方法的文檔字符串。
就如同__init__方法一樣,還有一個特殊的方法__del__,它在對象消逝的時候被調用。對象消逝即對象不再被使用,它所占用的內存將返回給系統作它用。在這個方法里面,我們只是簡單地把Person.population減1。
當對象不再被使用時,__del__方法運行,但是很難保證這個方法究竟在什么時候運行。如果你想要指明它的運行,你就得使用del語句,就如同我們在以前的例子中使用的那樣。
給C++/Java/C#程序員的注釋
Python中所有的類成員(包括數據成員)都是公共的 ,所有的方法都是有效的。
只有一個例外:如果你使用的數據成員名稱以雙下划線前綴 比如__privatevar,Python的名稱管理體系會有效地把它作為私有變量。
這樣就有一個慣例,如果某個變量只想在類或對象中使用,就應該以單下划線前綴。而其他的名稱都將作為公共的,可以被其他類/對象使用。記住這只是一個慣例,並不是Python所要求的(與雙下划線前綴不同)。
同樣,注意__del__方法與destructor的概念類似。
繼承
面向對象的編程帶來的主要好處之一是代碼的重用,實現這種重用的方法之一是通過繼承機制。繼承完全可以理解成類之間的類型和子類型關系。
假設你想要寫一個程序來記錄學校之中的教師和學生情況。他們有一些共同屬性,比如姓名、年齡和地址。他們也有專有的屬性,比如教師的薪水、課程和假期,學生的成績和學費。
你可以為教師和學生建立兩個獨立的類來處理它們,但是這樣做的話,如果要增加一個新的共有屬性,就意味着要在這兩個獨立的類中都增加這個屬性。這很快就會顯得不實用。
一個比較好的方法是創建一個共同的類稱為SchoolMember然后讓教師和學生的類繼承 這個共同的類。即它們都是這個類型(類)的子類型,然后我們再為這些子類型添加專有的屬性。
使 用這種方法有很多優點。如果我們增加/改變了SchoolMember中的任何功能,它會自動地反映到子類型之中。例如,你要為教師和學生都增加一個新的 身份證域,那么你只需簡單地把它加到SchoolMember類中。然而,在一個子類型之中做的改動不會影響到別的子類型。另外一個優點是你可以把教師和 學生對象都作為SchoolMember對象來使用,這在某些場合特別有用,比如統計學校成員的人數。一個子類型在任何需要父類型的場合可以被替換成父類 型,即對象可以被視作是父類的實例,這種現象被稱為多態現象。
另外,我們會發現在重用父類的代碼的時候,我們無需在不同的類中重復它。而如果我們使用獨立的類的話,我們就不得不這么做了。
在上述的場合中,SchoolMember類被稱為基本類或超類 。而Teacher和Student類被稱為導出類或子類 。
現在,我們將學習一個例子程序。
使用繼承





















































為 了使用繼承,我們把基本類的名稱作為一個元組跟在定義類時的類名稱之后。然后,我們注意到基本類的__init__方法專門使用self變量調用,這樣我 們就可以初始化對象的基本類部分。這一點十分重要——Python不會自動調用基本類的constructor,你得親自專門調用它。
我們還觀察到我們在方法調用之前加上類名稱前綴,然后把self變量及其他參數傳遞給它。
注意,在我們使用SchoolMember類的tell方法的時候,我們把Teacher和Student的實例僅僅作為SchoolMember的實例。
另 外,在這個例子中,我們調用了子類型的tell方法,而不是SchoolMember類的tell方法。可以這樣來理解,Python總是首先查找對應類 型的方法,在這個例子中就是如此。如果它不能在導出類中找到對應的方法,它才開始到基本類中逐個查找。基本類是在類定義的時候,在元組之中指明的。
一個術語的注釋——如果在繼承元組中列了一個以上的類,那么它就被稱作多重繼承 。
字符串
字符串是字符的序列。字符串基本上就是一組單詞。
我幾乎可以保證你在每個Python程序中都要用到字符串,所以請特別留心下面這部分的內容。下面告訴你如何在Python中使用字符串。
* 使用單引號(')
你可以用單引號指示字符串,就如同'Quote me on this'這樣。所有的空白,即空格和制表符都照原樣保留。
* 使用雙引號(")
在雙引號中的字符串與單引號中的字符串的使用完全相同,例如"What's your name?"。
* 使用三引號('''或""")
利用三引號,你可以指示一個多行的字符串。你可以在三引號中自由的使用單引號和雙引號。例如:





* 轉義符
假 設你想要在一個字符串中包含一個單引號('),那么你該怎么指示這個字符串?例如,這個字符串是What's your name?。你肯定不會用'What's your name?'來指示它,因為Python會弄不明白這個字符串從何處開始,何處結束。所以,你需要指明單引號而不是字符串的結尾。可以通過 轉義符 來完成這個任務。你用/'來指示單引號——注意這個反斜杠。現在你可以把字符串表示為'What/'s your name?'。
另一個表示這個特別的字符串的方法是"What's your name?",即用雙引號。類似地,要在雙引號字符串中使用雙引號本身的時候,也可以借助於轉義符。另外,你可以用轉義符//來指示反斜杠本身。
值得注意的一件事是,在一個字符串中,行末的單獨一個反斜杠表示字符串在下一行繼續,而不是開始一個新的行。例如:
"This is the first sentence./
This is the second sentence."
等價於"This is the first sentence. This is the second sentence."
* 自然字符串
如果你想要指示某些不需要如轉義符那樣的特別處理的字符串,那么你需要指定一個自然字符串。自然字符串通過給字符串加上前綴r或R來指定。例如r"Newlines are indicated by /n"。
* Unicode字符串
Unicode 是書寫國際文本的標准方法。如果你想要用你的母語如北印度語或阿拉伯語寫文本,那么你需要有一個支持Unicode的編輯器。類似地,Python允許你 處理Unicode文本——你只需要在字符串前加上前綴u或U。例如,u"This is a Unicode string."。
記住,在你處理文本文件的時候使用Unicode字符串,特別是當你知道這個文件含有用非英語的語言寫的文本。
* 字符串是不可變的
這意味着一旦你創造了一個字符串,你就不能再改變它了。雖然這看起來像是一件壞事,但實際上它不是。我們將會在后面的程序中看到為什么我們說它不是一個缺點。
* 按字面意義級連字符串
如果你把兩個字符串按字面意義相鄰放着,他們會被Python自動級連。例如,'What/'s' 'your name?'會被自動轉為"What's your name?"。
給C/C++程序員的注釋
在Python中沒有專門的char數據類型。確實沒有需要有這個類型,我相信你不會為此而煩惱。
給Perl/PHP程序員的注釋
記住,單引號和雙引號字符串是完全相同的——它們沒有在任何方面有不同。
給正則表達式用戶的注釋
一定要用自然字符串處理正則表達式。否則會需要使用很多的反斜杠。例如,后向引用符可以寫成'//1'或r'/1'。
私有變量
Python 對私有類成員有部分支持。任何象__spam這樣形式的標識符(至少有兩個前導下划線,至多有一個結尾下划線)目前被替換成 _classname__spam,其中classname是所屬類名去掉前導下划線的結果。這種攪亂不管標識符的語法位置,所以可以用來定義類私有的實 例、變量、方法,以及全局變量,甚至於保存對於此類是私有的其它類的實例。如果攪亂的名字超過255個字符可能會發生截斷。在類外面或類名只有下划線時不 進行攪亂。
名字攪亂的目的是給類一種定義“私有”實例變量和方法的簡單方法,不需擔心它的其它類會定義同名變量,也不怕類外的代碼弄亂實例 的變量。注意攪亂規則主要是為了避免偶然的錯誤,如果你一定想做的話仍然可以訪問或修改私有變量。這甚至是有用的,比如調試程序要用到私有變量,這也是為 什么這個漏洞沒有堵上的一個原因。(小錯誤:導出類和基類取相同的名字就可以使用基類的私有變量)。
注意傳遞給exec,eval()或 evalfile()的代碼不會認為調用它們的類的類名是當前類,這與global語句的情況類似,global的作用局限於一起字節編譯的代碼。同樣的 限制也適用於getattr() ,setattr()和delattr(),以及直接訪問__dict__的時候。
下面例子中的類實現了自己的__getattr__和__setattr__方法,把所有屬性保存在一個私有變量中,這在Python的新舊版本中都是可行的:











