我們下面的探討基於Python3,我實際測試使用的是Python3.2,Python3與Python2在類函數的類型上做了改變
1,類定義語法
Python類定義以關鍵字class開頭,一個類定義例子
1 class MyClass: 2 """this is an example""" 3 i = 123 4 def f(self): 5 return "hello world"
當類定義一進入的時候,也就是class關鍵字一遇到的時候,就開啟了一個新的名字空間(namespace),並且被作為當前的局部域。
只要沒有進入新的局部域,類定義里面的賦值語句和函數定義都會把名字綁定在作為當前域的這個class namespace中。而類定義結束的時候,這個當前域就退出了,會重新進入到類定義之前的局部域(基本上是全局域)。
類定義結束的時候,一個類對象(class object)也被創建了,並且這個類對象會以定義時使用的名字綁定在類定義之前的那個局部域中。
上面的類定義在類的namespace中綁定了三個對象
- i : int
- f : 一個函數對象
- __doc__: 一個str對象(第2行三引號對類進行注釋)
2,類對象和實例對象(Class Object and Instance Object)
類對象支持兩種操作,屬性引用(attribute)和實例化(instantiation)。
2.1 屬性引用
類名字空間的所有name都是這個類的屬性,可以通過ClassName.Attr來直接引用,可以去獲取類的屬性,也可以對其賦值修改類的屬性,也可以為類對象增加新的屬性。
如對於上面的類對象,可以如下操作:
1 def add(self, x, y): 2 return x+y 3 4 MyClass.i = 4321 #將屬性MyClass.i綁定到一個新的int對象 5 MyClass.add = add #添加一個新的屬性,綁定到一個函數對象上
2.2 實例化
類對象還支持的另一個操作是實例化,實例化的語法就像函數調用一樣,類名加上括號。
x = MyClass()
這創建了一個實例對象(instance object),並在當前域中用名字x和這個對象綁定。
從一個類對象實例化出一個實例對象后,也引入了一個實例對象名字空間,這個名字空間會用類對象的名字空間去初始化。
當然實例化的時候可以進行初始化設置,可以帶一些參數,這就需要類去定義__init__()方法:
1 class Complex: 2 def __init__(self, realpart, imagpart): 3 self.r = realpart 4 self.i = imagpart 5 c = Complex(2, -3) 6 print(c.r, c.i)
輸出:(2, -3)
[對實例對象來說,支持屬性的哪些操作,下面的的是可以為一個實例的屬性重新綁定對象,我記得django view中的request是可以增加屬性的 ]??
3,方法對象(Method Object)
可以這么理解:方法是屬於某一個對象的函數。(method is a function that "belongs to " an object)
對於Python3來說,在class中的函數屬性就是函數對象(function object),而實例中的函數類屬性則是方法對象(method object)
- 注:在Python2.7中class和instance中的函數屬性都是方法對象,一個未綁定(unbound),一個是綁定了實例對象的
對於我們上面定義的類MyClass以及MyClass的實例x,用Python3.2測試一下
>>> type(MyClass.f) <class 'function'> >>> x = MyClass() >>> type(x.f) <class 'method'>
可以看到在Python3中x.f是method而MyClass.f是function。
使用MyClass.f和x.f
1 print(MyClass.f(x)) 2 print(x.f())
會輸出兩行一樣的:hello world
記住,對於Python3來說,MyClass.f就是一個普通的函數對象,而它要求了一個參數self,根據函數的定義,當然我們可以給self傳進去任何一個類型的對象,事實證明也是可以的
print(MyClass.f(5))
依然會輸出:hello world
- 注意:如果使用Python2.7的話,這樣會報錯,因為Python2.7是把MyClass.f作為method的,而method調用第一個參數必須是這個類的實例對象。
現在我們再看看method object,簡稱為method。
method在調用時其第一個參數必須為method"所屬的"對象,但是我們實際使用的時候都是這樣做的:
>>>x.f()
其實編譯器大概是這么做的,首先它確定x.f是一個method(注:x.f也可以不是一個method而是一個function,下面我們會看到),然后它去搜尋x所屬的class的定義,找到f,然后由x和f去生成一個method object,並把x作為第一個參數進行調用。
現在我們試着把x.f給綁定到一個function上去,代碼如下:
1 class MyClass: 2 """this is an example""" 3 i = 1234 4 def f(self): 5 return 'hello world' 6 def add(self, x, y): 7 return x+y 8 9 x = MyClass() 10 x.f = MyClass.add 11 12 print(x.f(x,3,4)) 13 print(x.add(8, 8))
輸出:7
16
還是這個MyClass,我們在類MyClass中定義了一個add,我們已經知道這是function。
我們把實例屬性x.f綁定到函數對象MyClass.add,所以現在x.f是function而不是method了,第12行的調用也說明了這一點,如果沒有第一個參數x,是會報錯的,同樣的實例x也有method add,不用傳入x,第13行的調用說明了這點。