標籤:python 類 資料封裝 類和執行個體
使用模組
Python本身就內建了很多非常有用的模組,只要安裝安裝完畢,這些模組就可以立刻使用。
我們以內建的 sys 模組為例,編寫一個 hello 的模組:
‘a test module‘ # 一個字串表示文檔注釋,任何模組代碼的第一個字串都被視為模組的文檔注釋__author__=‘xiaoming‘ #作者名 可以刪除 上面的也可以不要import sys #匯入 sys 模組def test(): args = sys.argv if len(args)==1: print(‘Hello,world!‘) elif len(args)==2: print(‘Hello,%s‘%args[1]) else: print("Too many argumments!")if __name__ == ‘__main__‘: test()
sys模組有一個 argv 變數,用list 儲存了命令的所有參數。argv至少有一個元素,以你為第一個參數永遠是該 .py 檔案的名稱,例如:
運行
E:\Python>python lianxi.py 獲得的 sys.argv 就是 [‘lianxi.py‘]
運行
E:\Python>python lianxi.py Mingtian 獲得的sys。argv 就是[‘lianxi.py ‘,‘Mingtian‘]
最後,注意到這兩行代碼:
if __name__==‘__main__‘: test()
當我們在命令列運行 lianxi 模組檔案時,python 解譯器把一個特殊變數 __name__置為__main__,而如果在其他地方匯入該lianxi 模組時,if 前段將失敗,因此,這種if 測試可以讓一個模組通過命令列運行一個額外的代碼,最常見的就是運行測試。
用命令號運行 lianxi.py :
E:\Python>python lianxi.pyHello,world!
E:\Python>python lianxi.py MingtianHello,Mingtian
啟動python互動環境,再匯入練習模組:
>>> import lianxi>>> lianxi.test()Hello,world!
匯入時沒有列印 hello,world,因為沒事執行test()函數。調用test()函數才會列印 hello,world
範圍
在一個模組中,我們可能會定義很多函數和變數,擔憂的函數和變數我們希望給別人使用,有的函數和變數我們希望僅僅在模組內部使用。在Python中,是通過 _ 首碼來實現的。
正常的函數和變數名是公開的(public),可以被直接飲用,比如:abc,x123,PI等:
類似__xxx__這應的函數或變數就是非公開的(private),不應該被直接飲用, 比如_abc,__abc等:
之所以我們說,private函數和變數“不應該”被直接飲用,而不是“不能”被直接飲用,是因為Python並沒有一種方法可以完全限制訪問private函數或變數,但是,從變成習慣上不應該應用private函數或變數。
private函數或變數不應該被別人引用,那它們有什麼用呢?:
def _private_1(name): return ‘Hello, %s‘ %namedef _private_2(name): return ‘Hi, %s‘ %name def greeting(name): if len(name) > 3: return _private_1(name) else: return _private_2(name)
我們在模組裡公開greeting()
函數,而把內部邏輯用private函數隱藏起來了,這樣,調用greeting()
函數不用關心內部的private函數細節,這也是一種非常有用的代碼封裝和抽象的方法,即:
外部不需要引用的函數全部定義成private,只有外部需要引用的函數才定義為public。
物件導向編程
物件導向編程 ------ Object Oriented Programming,簡稱OOP,是一種程式設計思想。OOP把對象作為程式的基本單元,一個對象包含了資料和操作資料的函數。
面向過程的程式設計把電腦程式視為一系列的命令集合,及一組函數的順序執行。為了簡化程式設計,面向過程把函數繼續切為分子函數,即把大塊函數通過切割成小塊函數來降低體統的複雜度。
而物件導向的程式設計把電腦程式視為一組對象的集合,而每個對象都可以接受其他對象發過來的訊息,並處理這些訊息,電腦程式的執行 就是一系列訊息在各個對象之間傳遞。
在python中,所有資料類型都可以視為對象,當然也可以自訂對象。自訂的的資料類型就是物件導向中的類class的概念。
Eg:顯示一個學生的成績
(1)面向過程:
>>> std1 = {‘name‘:‘Micheal‘,‘score‘:98}>>> std2 = {‘name‘:‘Bob‘,‘score‘:81}>>> def print_score(std): print(‘%s:%s‘%(std[‘name‘],std[‘score‘]))
>>> print_score(std1)Micheal:98>>> print_score(std2)Bob:81
(2)採用物件導向的程式設計思想,我們首選思考的不是程式的執行流程,而是student這種資料類型應該被視為一個對象,這個對象擁有 name 和 score 這兩個屬性(Property)。如果要列印一個學生的成句,首相必須建立出這個學生對應的對象,然後,給對象發一個 print_score 訊息,讓自己把自己的資料列印出來。
class Student(object): def __init__(self,name,score): self.name = name self.score = score def print_score(self): print(‘%s:%s‘%(self.name,self.score))
給對象發訊息實際上就是調用對象對應的關聯函數,我們稱之為對象的方法(Method)。物件導向的程式寫出來就想這樣:
>>> bart = Student(‘Bart‘,59)>>> lisa = Student(‘Lisa‘,98)>>> bart.print_score()Bart:59>>> lisa.print_score()Lisa:98
類和執行個體
物件導向最重要的概念就是類(Class)和執行個體(Instance),必須牢記類是抽象的模板,比如Student類,而執行個體是根據類建立出來的一個個具體的“對象”,每個對象都擁有相同的方法,但各自的資料可能不同。
在Python中,定義類是通過class
關鍵字:
>>> class Student(object): pass
class
後面緊接著是類名,即Student
,類名通常是大寫開頭的單詞,緊接著是(object)
,表示該類是從哪個類繼承下來的,
通常,如果沒有合適的繼承類,就使用object
類,這是所有類最終都會繼承的類。
定義好了Student
類,就可以根據Student
類建立出Student
的執行個體,建立執行個體是通過類名+()實現的:
>>> bart = Student(‘xiaoming‘,66)>>> bart<__main__.Student object at 0x02CEAA70>>>> Student<class ‘__main__.Student‘>
可以看到,變數bart
指向的就是一個Student
的執行個體,後面的0x10a67a590
是記憶體位址,每個object的地址都不一樣,而Student
本身則是一個類。
可以自由地給一個執行個體變數綁定屬性,比如,給執行個體bart
綁定一個name
屬性:
>>> bart.name = ‘xaioming‘>>> bart.name‘xaioming‘
由於類可以起到模板的作用,因此,可以在建立執行個體的時候,把一些我們認為必須綁定的屬性強制填寫進去。通過定義一個特殊的__init__
方法,在建立執行個體的時候,就把name
,score
等屬性綁上去:
class Student(object): def __init__(self,name,score): self.name = name self.score = score
注意__init__方法的第一個參數永遠是self,表示建立的執行個體的本身,因此,在__init__方法內部,就可以把各種屬性綁定帶self,因為self就指向建立的執行個體本身。有了 __inlt__方法在建立執行個體的時候,就不能傳入空的參數了,必須傳入與__init__方法匹配的參數,但self不需要傳,Python解譯器自己會把執行個體變數穿進去:
>>> bart = Student(‘xiaoming‘,66)>>> bart.name‘xiaoming‘>>> bart.score66
和普通的函數相比,在類中定義的函數只有一點不同,就是第一個參數永遠是執行個體變數self,並且,調用時,不用傳遞該參數。除此之外,類的方法和普通函數沒有什麼區別,所以,你任然可以用預設參數、可變參數、關鍵字參數和命名關鍵字參數
資料封裝
物件導向編程的一個重要特點就是資料封裝。在上面的student類中,每個執行個體就擁有各自的name 和 score 這些資料。
我們可以通過函數來訪問這些資料,比如列印一個學生的成績:
def print_score(self): print(‘%s:%s‘%(self.name,self.score))
>>> def print_score(std): print(‘%s: %s‘ % (std.name, std.score))
>>> print_score(bart)Bart Simpson: 59
既然Student執行個體本身就擁有這些資料,要訪問這些資料,就沒有必要從外面的函數去訪問,可以直接在Student類的內部定義訪問資料的函數,這樣,就把‘‘資料"給封裝起來了。這些封裝資料的函數是和Student類本身關聯起來的,我們稱之為類的方法:
class Student(object): def __init__(self,name,score): self.name = name self.score = score def print_score(self): print(‘%s:%s‘%(self.name,self.score))>>> bart=Student(‘xiaoming‘,99)>>> bart.print_score()xiaoming:99
訪問限制
在class內部,可以有屬性和方法,而外部代碼可以通過直接調用執行個體變數的方法來操作資料,這樣,就隱藏了內部的複雜 邏輯。
但是,從前面Student類的定義來看,外部代碼還是可以自由地修改一個執行個體的name,score屬性:
>>> bart=Student(‘Bart‘,98)>>> bart.score98>>> bart.score=59>>> bart.score59
如果要讓內部屬性,可以把屬性的名稱前加上兩個底線__,在python中,執行個體的變數名如果以__開頭,就變成了一個私人變數(private),只有內部可以訪問,外部不能訪問,所以,我們把Student類改一改:
class Student(object): def __init__(self,name,score): self.__name = name self.__score = score def print_score(self): print(‘%s:%s‘%(self.__name,self.__score)) def get_grade(self): if self.score>=90: return ‘A‘ elif self.score>=60: return ‘B‘ else: return ‘C‘
改完後,對於外部代碼來說,沒什麼變動,但是已經無法從外部存取執行個體變數.__name
和執行個體變數.__score
了:
>>> bart=Student(‘Bart‘,98) >>> bart.__name Traceback (most recent call last) : File "<pyshell#11>", line 1, in <module> bart.__nameAttributeError: ‘Student‘ object has no attribute ‘__name‘
這樣就確保了外部代碼不能隨意修改對象內部的狀態,這樣通過訪問限制的保護,代碼更加健壯。
但是如果外部代碼要擷取name和score怎麼辦?可以給Student 類增加 get_name 和 get_score 這樣的方法:
class Student(object): ... def get_name(self): return self.__name def get_score(self): return self.__score
如果又要允許外部代碼修改score怎麼辦?可以再給Student類增加set_score
方法:
class Student(object): ... def set_score(self, score): self.__score = score
在方法中,可以對參數做檢查,避免傳入無效的參數:
class Student(object): ... def set_score(self, score): if 0 <= score <= 100: self.__score = score else: raise ValueError(‘bad score‘)
需要注意的是,在Python中,變數名類似__xxx__
的,也就是以雙底線開頭,並且以雙底線結尾的,是特殊變數,特殊變數是可以直接存取的,不是private變數,所以,不能用__name__
、__score__
這樣的變數名。
有些時候,我們會看到以一個底線開頭的執行個體變數名,比如_name
,這樣的執行個體變數外部是可以訪問的,但是,按照約定俗成的規定,當你看到這樣的變數時,意思就是,“雖然我可以被訪問,但是,請把我視為私人變數,不要隨意訪問”。
雙底線開頭的執行個體變數是不是一定不能從外部存取呢?其實也不是。不能直接存取__name
是因為Python解譯器對外把__name
變數改成了_Student__name
,所以,仍然可以通過_Student__name
來訪問__name
變數:
>>> bart._Student__name‘Bart Simpson‘
因為不同版本的Python解譯器可能會把__name
改成不同的變數名。
總的來說就是,Python本身沒有任何機制阻止你幹壞事,一切全靠自覺。
本文出自 “啟思·朝聖者” 部落格,請務必保留此出處http://dearch.blog.51cto.com/10423918/1762873
據廖雪峰python3教程----python學習第十二天