標籤:
類和執行個體
類是對象建立執行個體的模板,而執行個體則是對象的實體。類使用class關鍵字定義:
class MyClass:
? ? pass
python中建立執行個體直接使用工廠函數(類名加上一對括弧),和其他的語言使用new關鍵字有所不同:
my_obj = MyClass()
一般來說,類名以大寫字母開頭,而對象名則以小寫字母或者底線開頭。
執行個體化對象時,會執行類定義中的__init__()方法,該方法執行時包含執行個體的各種初始化操作。
方法和函數的區別:方法由對象調用,在方法定義中,第一個參數必須是顯示的self,表示當前對象,而函數則不傳入當前對象self,可以說方法是特殊的函數,他指定了調用對象,並且可以獲得調用對象的屬性。函數和方法都是function類的執行個體:
class MyClass(object):
? ? def __init__(self):
? ? ? ? pass
? ? def method1(a,b):
? ? ? ? print(a+b)
? ? def method2(self,a,b):
? ? ? ? print(a+b)
obj = MyClass()
MyClass.method1(1,2)
obj.method2(1,2)
print(type(MyClass.method1))
print(type(MyClass.method2))
上述代碼中,obj.method2就是方法,而MyClass.method1是一個普通的函數,為什麼要使用MyClass.method1是因為method1的命名空間包含在MyClass中,而使用obj.method2的原因是因為method2是obj所在類的一個方法,並obj作為method1調用的對象。
從上述代碼還可以延生出以下結論:
1. MyClass.method2(1,2)將會產生錯誤:TypeError: method2() takes exactly 3 arguments (2 given)。 而MyClass也是一個對象,執行MyClass.method2()時,並不能將MyClass作為self代表的當前對象,這是因為執行個體的方法必須存在於類中,而執行個體的屬性則存在於執行個體本身,MyClass調用method2不成功是因為MyClass所在的類:type並沒有該方法,所以MyClass.method2()僅僅是執行MyClass命名空間中的一個函數,而該函數要求傳入一個對象,所以導致了TypeError錯誤。
2. obj.method1(1,2)將會產生錯誤:TypeError: method1() takes exactly 2 positional arguments (3 given)。雖然調用報錯,但是報錯的原因僅僅是因為參數數量不匹配,由此可見,一個對象調用方法時,該方法必須要顯式的第一個對象為self。並且,obj調用時,並沒有使用MyClass命名空間,因為obj是MyClass的執行個體,將會整合MyClass的命名空間。
對象的屬性
在python中,執行個體可以在任何時刻添加對象,即使在類中並沒有定義該屬性,但是該語句並不會有任何錯誤,並且在之後一樣可以調用該屬性,這樣的特性可能非常的方便,但是也隱藏了很多弊端,容易對已知的對象傳入不必要的屬性,從而搞得一團糟:
class MyClass:
? ? def __init__(self,a,b):
? ? ? ? self.a = a
? ? ? ? self.b = b
obj = MyClass(1,2)
print(obj.a)
obj.a = 3
print(obj.a)
def a_method(self,c):
? ? self.c = c
from types import MethodType
obj.a_method = MethodType(a_method,obj)
obj.a_method(4)
print(obj.c)
上述通過類定義之外的途徑和對象添加方法和屬性,在其他的同類的對象中是不具有的,可以看成是ruby中單件類類似的技巧,通過上述代碼給obj添加的方法,實際上存在於obj的單件類中。
通過__slots__可以限制類的屬性,以防止對執行個體添加不必要的變數:
class MyClass:
? ? __slots___ = (‘a’,’b‘)
? ? ...
此時,如果再給obj添加其他的屬性將會告知MyClass中沒有該屬性的錯誤,從而限制了對象的屬性。
對象的屬性是不再能被任意添加了,但是仍然可以在外部可以被外部代碼輕鬆訪問,通過在變數前添加兩個底線來表示私人變數:
class Student:
? ? __slots__ = (‘score‘,‘grade‘)
? ? def __init__(self,score):
? ? ? ? self.score = score
? ? def getScore(self):
? ? ? ? if self.score >= 90:
? ? ? ? ? ? ?self.grade = ‘A‘
? ? ? ? elif self.score >= 60:
? ? ? ? ? ? self.grade = ‘B‘
? ? ? ? elif self.score < 60:
? ? ? ? ? ? self.grade = ‘C‘
? ? ? ? return self.grade
xiaoming = Student(47)
xiaoming.score = 90
print(xiaoming.getScore())
在上述代碼中,雖然在初始化小明的成績的時候輸入了47分,但是仍然可以在外部對該成績進行修改,由於小明懂了一點python,瞬間就從差等生變成優等生,這樣的結果是不希望被看到的。將score改為__score後:
class Student:
? ? __slots__ = (‘__score‘,‘grade‘)
? ? def __init__(self,score):
? ? ? ? self.__score = score
? ? def getGrade(self):
? ? ? ? if self.__score >= 90:
? ? ? ? ? ? self.grade = ‘A‘
? ? ? ? elif self.__score >= 60:
? ? ? ? ? ? self.grade = ‘B‘
? ? ? ? elif self.__score < 60:
? ? ? ? ? ? self.grade = ‘C‘
? ? ? ? return self.grade
此時,小明的投機取巧修改自己分數的手段就不再生效。訪問xiaoming.__score時,將出現以下錯誤:AttributeError: ‘Student‘ object has no attribute ‘__score‘
儘管如此,其實小明還是可以這樣做:
xiaoming._Student__score = 100
因為在使用雙底線時,python只不過是講變數的名字之前加入了_Classname首碼以避免被直接存取到(不同版本的解譯器會有所不同),而不是真正的不可訪問,python中並沒有真正不可操作的機制,而類似於_Student這些單底線開頭的變數名,雖然可以被外部存取到,但是最好不要這樣做,單底線的意思就是,雖然我不是私人變數,但是請你把我當成私人變數,這或許是對python中沒有私人變數的一種妥協吧。
此時,我也沒有辦法來對付小明了。這是很有意思的故事,在Ruby中,任何執行個體變數在設定set方法和get方法之前,都是不能被外部存取到的,只有通過給私人變數設定setter方法或者getter方法,該變數才真正的成為屬性,總之為了執行個體變數成為屬性要做一些必要工作。而python中,幾乎不能阻止一個對象訪問在類中定義的變數,甚至如果你不用__slots__,都無法阻止添加各種屬性到對象中,我們要做的工作反而變成想方設法讓屬性不能被訪問到。
使用真正的屬性
python也可以和其他語言一樣,通過@property修飾器,實現getter、setter方法從而訪問和設定的屬性,從而可以“掩耳盜鈴”地說,你不能設定該屬性:
class Student:
? ? __slots__ = (‘_score‘,‘_grade‘)
? ? def __init__(self,score):
? ? ? ? self._score = score
? ? @property
? ? def score(self):
? ? ? ? return _score
? ? @property
? ? def grade(self):
? ? ? ? if self._score >= 90:
? ? ? ? ? ? self._grade = ‘A‘
? ? ? ? elif self._score >= 60:
? ? ? ? ? ? self._grade = ‘B‘
? ? ? ? elif self._score < 60:
? ? ? ? ? ? self._grade = ‘C‘
? ? ? ? return self._grade
當使用了@property修飾器之後,其實就是實現了getter方法,使外部可以被看到,如果想要設定getter方法,則再添加:
? ? @score.setter
? ? def score(self,value):
? ? ? ? score._score = value
和其他語言一樣,方法名字就是屬性名稱字,小明可以通過xiaoming.score來看到自己的成績,當沒有定義setter方法時,小明是不能通過xiaoming.score來修改自己的成績的,但是這是不是就意味著小明真的不能改了呢?其實通過上面的代碼已經可以看到,python內部的變數名字並不是score,而是_score,這就是為什麼我說“掩耳盜鈴”的原因,小明依然可以通過xiaoming._score來修改自己的成績。總之,python中是不會真正阻止一個對象訪問類定義中的變數的,這實在有點令人苦惱。?
今天是不完整的一天
Python學習_11_類和執行個體