Python類中的特殊方法


類中的特殊方法

一般說來,特殊的方法都被用來模仿某個行為。例如,如果你想要為你的類使用x[key]這樣的索引操作(就像列表和元組一樣),那么你只需要實現__getitem__()方法就可以了。想一下,Python就是對list類這樣做的!

下面這個表中列出了一些有用的特殊方法。如果你想要知道所有的特殊方法,你可以在《Python參考手冊》中找到一個龐大的列表。

名稱                     說明
---------------------------------------------------------
__init__ (self,...)      這個方法在新建對象恰好要被返回使用之前被調用。
__del__ (self)           恰好在對象要被刪除之前調用。
__str__ (self)           在我們對對象使用print語句或是使用str()的時候調用。
__lt__ (self,other)      當使用 小於 運算符( < )的時候調用。類似地,對於所有的運算符( + > 等等)都有特殊的方法。
__getitem__ (self,key)   使用x[key]索引操作符的時候調用。
__len__ (self)           對序列對象使用內建的len()函數的時候調用。
__repr__ (s)             repr()  and  `...` conversions
__cmp__ (s, o)           Compares s to o  and  returns  < 0, 0,  or   > 0. 
                        Implements 
> < ==  etc...
__hash__ (s)             Compute a  32  bit hash code; hash()  and  dictionary ops
__nonzero__ (s)          Returns 0  or   1   for  truth value testing
__getattr__ (s, name)    called when attr lookup doesn ' t find 
__setattr__ (s, name, val) called when setting an attr
                        (inside, don
' t use "self.name = value"
                        use  " self.__dict__[name] = val " )
__delattr__ (s, name)    called to delete attr  < name >
__call__ (self,  * args)   called when an instance  is  called as function.

exec和eval語句

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

>>>   exec   ' print "Hello World" '
Hello World

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

>>>  eval( ' 2*3 ' )
6  

assert語句

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

 

>>>  mylist  =  [ ' item ' ]
>>>   assert  len(mylist)  >=   1
>>>  mylist.pop()
' item '
>>>   assert  len(mylist)  >=   1
Traceback (most recent call last):
  File 
" " , line  1 in  ?
AssertionError repr函數
repr函數用來取得對象的規范字符串表示。反引號(也稱轉換符)可以完成相同的功能。注意,在大多數時候有eval(repr(object)) == object。
>>>  i  =  []
>>>  i.append( ' item ' )
>>>  `i`
" ['item'] "
>>>  repr(i)
" ['item'] "

基本上,repr函數和反引號用來獲取對象的可打印的表示形式。你可以通過定義類的__repr__方法來控制你的對象在被repr函數調用的時候返回的內容。

類和實例變量

有兩種類型的域 —— 類的變量和對象的變量,它們根據是類還是對象擁有這個變量而區分。

類的變量 由一個類的所有對象(實例)共享使用。只有一個類變量的拷貝,所以當某個對象對類的變量做了改動的時候,這個改動會反映到所有其他的實例上。

對象的變量 由類的每個對象/實例擁有。因此每個對象有自己對這個域的一份拷貝,即它們不是共享的,在同一個類的不同實例中,雖然對象的變量有相同的名稱,但是是互不相關的。通過一個例子會使這個易於理解。

# !/usr/bin/python
#
 Filename: objvar.py

class  Person:
    
''' Represents a person. '''
    population 
=  0

    
def   __init__ (self, name):
        
''' Initializes the person's data. '''
        self.name 
=  name
        
print   ' (Initializing %s) '   %  self.name

        
#  When this person is created, he/she
         #  adds to the population
        Person.population  +=   1

    
def   __del__ (self):
        
''' I am dying. '''
        
print   ' %s says bye. '   %  self.name

        Person.population 
-=   1

        
if  Person.population  ==  0:
            
print   ' I am the last one. '
        
else :
            
print   ' There are still %d people left. '   %  Person.population

    
def  sayHi(self):
        
''' Greeting by the person.

        Really, that's all it does.
'''
        
print   ' Hi, my name is %s. '   %  self.name

    
def  howMany(self):
        
''' Prints the current population. '''
        
if  Person.population  ==   1 :
            
print   ' I am the only person here. '
        
else :
            
print   ' We have %d persons here. '   %  Person.population

swaroop 
=  Person( ' Swaroop ' )
swaroop.sayHi()
swaroop.howMany()

kalam 
=  Person( ' Abdul Kalam ' )
kalam.sayHi()
kalam.howMany()

swaroop.sayHi()
swaroop.howMany()輸出

___FCKpd___5nbsp;python objvar.py
(Initializing Swaroop)
Hi, my name 
is  Swaroop.
I am the only person here.
(Initializing Abdul Kalam)
Hi, my name 
is  Abdul Kalam.
We have 
2  persons here.
Hi, my name 
is  Swaroop.
We have 
2  persons here.
Abdul Kalam says bye.
There are still 
1  people left.
Swaroop says bye.
I am the last one.

這是一個很長的例子,但是它有助於說明類與對象的變量的本質。這里,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類被稱為導出類或子類 。

現在,我們將學習一個例子程序。
使用繼承

 

# !/usr/bin/python
#
 Filename: inherit.py

class  SchoolMember:
    
''' Represents any school member. '''
    
def   __init__ (self, name, age):
        self.name 
=  name
        self.age 
=  age
        
print   ' (Initialized SchoolMember: %s) '   %  self.name

    
def  tell(self):
        
''' Tell my details. '''
        
print   ' Name:"%s" Age:"%s" '   %  (self.name, self.age),

class  Teacher(SchoolMember):
    
''' Represents a teacher. '''
    
def   __init__ (self, name, age, salary):
        SchoolMember.
__init__ (self, name, age)
        self.salary 
=  salary
        
print   ' (Initialized Teacher: %s) '   %  self.name

    
def  tell(self):
        SchoolMember.tell(self)
        
print   ' Salary: "%d" '   %  self.salary

class  Student(SchoolMember):
    
''' Represents a student. '''
    
def   __init__ (self, name, age, marks):
        SchoolMember.
__init__ (self, name, age)
        self.marks 
=  marks
        
print   ' (Initialized Student: %s) '   %  self.name

    
def  tell(self):
        SchoolMember.tell(self)
        
print   ' Marks: "%d" '   %  self.marks

=  Teacher( ' Mrs. Shrividya ' 40 30000 )
=  Student( ' Swaroop ' 22 75 )

print   #  prints a blank line

members 
=  [t, s]
for  member  in  members:
    member.tell() 
#  works for both Teachers and Students輸出

$ python inherit.py
(Initialized SchoolMember: Mrs. Shrividya)
(Initialized Teacher: Mrs. Shrividya)
(Initialized SchoolMember: Swaroop)
(Initialized Student: Swaroop)

Name:
" Mrs. Shrividya "  Age: " 40 "  Salary:  " 30000 "
Name:
" Swaroop "  Age: " 22 "  Marks:  " 75 "

為 了使用繼承,我們把基本類的名稱作為一個元組跟在定義類時的類名稱之后。然后,我們注意到基本類的__init__方法專門使用self變量調用,這樣我 們就可以初始化對象的基本類部分。這一點十分重要——Python不會自動調用基本類的constructor,你得親自專門調用它。

我們還觀察到我們在方法調用之前加上類名稱前綴,然后把self變量及其他參數傳遞給它。

注意,在我們使用SchoolMember類的tell方法的時候,我們把Teacher和Student的實例僅僅作為SchoolMember的實例。

另 外,在這個例子中,我們調用了子類型的tell方法,而不是SchoolMember類的tell方法。可以這樣來理解,Python總是首先查找對應類 型的方法,在這個例子中就是如此。如果它不能在導出類中找到對應的方法,它才開始到基本類中逐個查找。基本類是在類定義的時候,在元組之中指明的。

一個術語的注釋——如果在繼承元組中列了一個以上的類,那么它就被稱作多重繼承 。

字符串

字符串是字符的序列。字符串基本上就是一組單詞。

我幾乎可以保證你在每個Python程序中都要用到字符串,所以請特別留心下面這部分的內容。下面告訴你如何在Python中使用字符串。

* 使用單引號(')

你可以用單引號指示字符串,就如同'Quote me on this'這樣。所有的空白,即空格和制表符都照原樣保留。
* 使用雙引號(")

在雙引號中的字符串與單引號中的字符串的使用完全相同,例如"What's your name?"。
* 使用三引號('''或""")

利用三引號,你可以指示一個多行的字符串。你可以在三引號中自由的使用單引號和雙引號。例如:

 

''' This is a multi-line string. This is the first line.
This is the second line.
"What's your name?," I asked.
He said "Bond, James Bond."
'''

 

* 轉義符

假 設你想要在一個字符串中包含一個單引號('),那么你該怎么指示這個字符串?例如,這個字符串是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的新舊版本中都是可行的:

class  VirtualAttributes:
    
__vdict   =  None
    
__vdict_name   =  locals().keys()[0]
     
    
def   __init__ (self):
        self.
__dict__ [self. __vdict_name =  {}
    
    
def   __getattr__ (self, name):
        
return  self. __vdict [name]
    
    
def   __setattr__ (self, name, value):
        self.
__vdict [name]  =  value

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM