第十章的內容是解決問題————編寫一個Python腳本。在我的電腦上因為Zip命令不能正常工作所以無法給出演示。該章給出了很有意義的編程思路,對以后學習和工作都有不錯的參考意義,這部分有興趣的同學還是自己去看原版教程吧。
這篇博客結合個人筆記整理了《簡明Python教程》第十一章到第十二章知識點。主要內容包括以下部分:
11.面向對象的編程
12.輸入/輸出
11.面向對象編程
簡介:
到目前為止,在我們的程序中,我們都是根據操作數據的函數或語句塊來設計程序的。這被稱 為面向過程的編程。還有一種把數據和功能結合起來,用稱為對象的東西包裹起來組織程序 的方法。這種方法稱為面向對象的編程理念。在大多數時候你可以使用過程性編程,但是有些時候當你想要編寫大型程序或是尋求一個更加合適的解決方案的時候,你就得使用面向對象 的編程技術。
類和對象是面向對象編程的兩個主要方面。類創建一個新類型,而對象這個類的 實例。這類似于你有一個int類型的變量,這存儲整數的變量是int類的實例(對象)。
對象可以使用普通的屬于對象的變量存儲數據。屬于一個對象或類的變量被稱為域。對象也可以使用屬于類的函數來具有功能。這樣的函數被稱為類的方法。這些術語幫助我們把它們與孤立的函數和變量區分開來。域和方法可以合稱為類的屬性。
域有兩種類型——屬于每個實例/類的對象或屬于類本身。它們分別被稱為實例變量和
類變量。
類使用class關鍵字創建。類的域和方法被列在一個縮進塊中。
關鍵字:面向過程的編程 面向對象的編程 類 對象 域 方法 class
11.1 self:
類的方法與普通的函數只有一個特別的區別——它們必須有一個額外的第一個參數名稱,但是在調用這個方法的時候你不為這個參數賦值,Python會提供這個值。這個特別的變量指對象本身,按照慣例它的名稱是self。
雖然你可以給這個參數任何名稱,但是強烈建議你使用self這個名稱——其他名稱都是不贊成 你使用的。使用一個標準的名稱有很多優點——你的程序讀者可以迅速識別它,如果使用self 的話,還有些IDE(集成開發環境)也可以幫助你。
你一定很奇怪Python如何給self賦值以及為何你不需要給它賦值。舉一個例子會使此變得清晰。假如你有一個類稱為MyClass和這個類的一個實例MyObject。當你調用這個對象的方法 MyObject.method(arg1, arg2)的時候,這會由Python自動轉為MyClass.method(MyObject, arg1, arg2)——這就是self的原理了。
這也意味著如果你有一個不需要參數的方法,你還是得給這個方法定義一個self參數 。
關鍵字:區別 額外的第一個參數名稱 self 指對象本身 不用賦值
類:
創建一個類
例11.1
#!/usr/bin/python #Filename:simplestclass.py class person: pass p=person() print p
輸出
# python simplestclass.py <__main__.person instance at 0x7ff1cc9121b8>
如何工作:
我們使用class語句后跟類名,創建了一個新的類。這后面跟著一個縮進的語句塊形成類體。在 這個例子中,我們使用了一個空白塊,它由pass語句表示。
接下來,我們使用類名后跟一對圓括號來創建一個對象(實例)。為了驗證,我們簡單地打印了這個變量的類型。它告訴我們我們已經在main模塊中有了一個Person類的實例。
11.2 對象的方法:
我們已經討論了類或者對象可以擁有像函數一樣的方法,這些方法與函數的區別只是一個額外的 self變量。
使用對象的方法
例11.2
#!/usr/bin/python #Filename:method.py class person: def sayHi(self): print 'Hello,how are you?' p=person() p.sayHi()
輸出:
Hello,how are you?
# python method.py Hello,how are you?
如何工作:
這里我們看到了self的用法。注意sayHi方法沒有任何參數,但仍然在函數定義時有self。
11.3 init方法
在Python的類中有很多方法的名字有特殊的重要意義?,F在我們將學習init方法的意義。
init方法在類的一個對象被建立時,馬上運行。這個方法可以用來對你的對象做一些你希 望的 初始化 。注意,這個名稱的開始和結尾都是雙下劃線。
使用init方法
例11.3
#!/usr/bin/python #Filename:class_init.py class person: def __init__(self,name): self.name=name def sayHi(self): print 'Hello.my name is',self.name p=person('Swaroop') p.sayHi()
輸出:
# python class_init.py Hello,my name is Swaroop
如何工作:
這里,我們把init方法定義為取一個參數name(以及普通的參數self)。在這個init里, 我們只是創建一個新的域,也稱為name。注意它們是兩個不同的變量,盡管它們有相同的名字。點號使我們能夠區分它們。
最重要的是,我們沒有專門調用init方法,只是在創建一個類的新實例的時候,把參數包 括在圓括號內跟在類名后面,從而傳遞給init方法。這是這種方法的重要之處。
現在,我們能夠在我們的方法中使用self.name域。這在sayHi方法中得到了驗證。
注:事先定義好init方法,不需要專門調用就能完成所需的初始化工作。
11.4 類和對象的變量(原書中是類和對象的方法,應是筆誤)
我們已經討論了類與對象的功能部分,現在我們來看一下它的數據部分。事實上,它們只是與 類和對象的名稱空間 綁定 的普通變量,即這些名稱只在這些類與對象的前提下有效。
有兩種類型的 域 ——類的變量和對象的變量,它們根據是類還是對象 擁有 這個變量而區分。
類的變量 由一個類的所有對象(實例)共享使用。只有一個類變量的拷貝,所以當某個對象 對類的變量做了改動的時候,這個改動會反映到所有其他的實例上。
對象的變量 由類的每個對象或實例擁有。因此每個對象有自己對這個域的一份拷貝,即它們不 是共享的,在同一個類的不同實例中,雖然對象的變量有相同的名稱,但是是互不相關的。通 過一個例子會使這個易于理解。
使用類和對象的變量
例11.4
#!/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 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()
輸出:
# 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語句,就如同我們在以前的例子中使用的那樣。
注:對比方法調用和輸出可以看出del的執行同樣不需要專門調用。在對象消逝或者說進程的生命結束時del就自動運行了。
11.5 繼承:
面向對象的編程帶來的主要好處之一是代碼的重用,實現這種重用的方法之一是通過 繼承 機 制。繼承完全可以理解成類之間的 類型和子類型 關系。
假設你想要寫一個程序來記錄學校之中的教師和學生情況。他們有一些共同屬性,比如姓名、 年齡和地址。他們也有專有的屬性,比如教師的薪水、課程和假期,學生的成績和學費。
你可以為教師和學生建立兩個獨立的類來處理它們,但是這樣做的話,如果要增加一個新的共 有屬性,就意味著要在這兩個獨立的類中都增加這個屬性。這很快就會顯得不實用。
一個比較好的方法是創建一個共同的類稱為SchoolMember然后讓教師和學生的類 繼承 這個共 同的類。即它們都是這個類型(類)的子類型,然后我們再為這些子類型添加專有的屬性。
使用這種方法有很多優點。如果我們增加或改變了SchoolMember中的任何功能,它會自動地反 映到子類型之中。例如,你要為教師和學生都增加一個新的身份證域,那么你只需簡單地把它 加到SchoolMember類中。然而,在一個子類型之中做的改動不會影響到別的子類型。另外一個 優點是你可以把教師和學生對象都作為SchoolMember對象來使用,這在某些場合特別有用,比 如統計學校成員的人數。一個子類型在任何需要父類型的場合可以被替換成父類型,即對象可 以被視作是父類的實例,這種現象被稱為多態現象。
另外,我們會發現在 重用 父類的代碼的時候,我們無需在不同的類中重復它。而如果我們使 用獨立的類的話,我們就不得不這么做了。
在上述的場合中,SchoolMember類被稱為 基本類 或 超類 。而Teacher和Student類被稱為 導出 類 或 子類 。
使用繼承
例11.5
#!/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 t=Teacher('Mrs.Shrividya',40,30000) s=Student('Swaroop',22,75) print schmembers=[t,s] for schmember in schmembers: schmember.tell()
輸出:
# 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總是首先查找對應類型的方法,在這個例子中就是如此。如果它不能在導 出類中找到對應的方法,它才開始到基本類中逐個查找?;绢愂窃陬惗x的時候,在元組之 中指明的。
一個術語的注釋——如果在繼承元組中列了一個以上的類,那么它就被稱作 多重繼承 。
注:在程序源碼的最后三行中,可以看出Python程序到底有多接近人類語言——
方法一: members = [t, s]
for member in members:
member.tell() # works for both Teachers and Students
方法二: schmembers=[t,s]
for schmember in schmembers:
schmember.tell()
剛開始我以為members作為一個數組,member代表數組元素一定是系統特定的格式。結果我自創了另外一個單詞schmember,結果可以同理使用。Python這方面真的很夸張,非常非常像自然語言。
概括:
我們已經研究了類和對象的多個內容以及與它們相關的多個術語。通過本章,你已經了解了面 向對象的編程的優點和缺陷。Python是一個高度面向對象的語言,理解這些概念會在將來有助 于你進一步深入學習Python。
接下來,我們將學習如何處理輸入/輸出已經如何用Python訪問文件。
12輸入/輸出
在很多時候,你會想要讓你的程序與用戶(可能是你自己)交互。你會從用戶那里得到輸入, 然后打印一些結果。我們可以分別使用raw_input和print語句來完成這些功能。對于輸出,你也 可以使用多種多樣的str(字符串)類。例如,你能夠使用rjust方法來得到一個按一定寬度右對 齊的字符串。利用help(str)獲得更多詳情。
另一個常用的輸入/輸出類型是處理文件。創建、讀和寫文件的能力是許多程序所必需的,我 們將會在這章探索如何實現這些功能。
12.1 文件
你可以通過創建一個file類的對象來打開一個文件,分別使用file類的read、readline或write方法來 恰當地讀寫文件。對文件的讀寫能力依賴于你在打開文件時指定的模式。最后,當你完成對文 件的操作的時候,你調用close方法來告訴Python我們完成了對文件的使用
使用文件
例12.1
#!/usr/bin/python #Filename:using_file.py poem='''\ Programming is fun When the work is done if you wanna make you work also fun: use Python! ''' f=file('poem.txt','w') f.write(poem) f.close() f=file('poem.txt') while True: line=f.readline() if len(line)==0: break print line, f.close()
輸出:
# python using_file.py Programming is fun When the work is done if you wanna make you work also fun: use Python!
如何工作:
首先,我們通過指明我們希望打開的文件和模式來創建一個file類的實例。模式可以為讀模式 (’r’)、寫模式(’w’)或追加模式(’a’)。事實上還有多得多的模式可以使用,你可以使用 help(file)來了解它們的詳情。
我們首先用寫模式打開文件,然后使用file類的write方法來寫文件,最后我們用close關閉這個文 件。
接下來,我們再一次打開同一個文件來讀文件。如果我們沒有指定模式,讀模式會作為默認的 模式。在一個循環中,我們使用readline方法讀文件的每一行。這個方法返回包括行末換行符 的一個完整行。所以,當一個 空的 字符串被返回的時候,即表示文件末已經到達了,于是我 們停止循環。
注意,因為從文件讀到的內容已經以換行符結尾,所以我們在print語句上使用逗號來消除自動 換行。最后,我們用close關閉這個文件。
現在,來看一下poem.txt文件的內容來驗證程序確實工作正常了。
# cat poem.txt Programming is fun When the work is done if you wanna make you work also fun: use Python!
筆記:
這里我認為最需要理解的是語句f=file(‘poem.txt’,’w’)
還記得之前說過的Python幾乎把一切都當做對象來處理嗎。這里file是文件類,f是對象,是file類的實例,poem.txt用來給f的一個域賦值。而之后f.write(),f.close()都是類的方法。
12.2 儲存器
Python提供一個標準的模塊,稱為pickle。使用它你可以在一個文件中儲存任何Python對象,之 后你又可以把它完整無缺地取出來。這被稱為 持久地 儲存對象。
還有另一個模塊稱為cPickle,它的功能和pickle模塊完全相同,只不過它是用C語言編寫的,因 此要快得多(比pickle快1000倍)。你可以使用它們中的任一個,而我們在這里將使用cPickle模 塊。記住,我們把這兩個模塊都簡稱為pickle模塊。
儲存和取儲存
例12.2
#!/usr/bin/python #Filename:pickling.py import cPickle as p shoplistfile='shoplist.data' shoplist=['apple','mango','carrot'] f=file(shoplistfile,'w') p.dump(shoplist,f) f.close() del shoplist f=file(shoplistfile) storedlist=p.load(f) f.close() print storedlist
輸出:
# python pickling.py ['apple', 'mango', 'carrot']
如何工作:
首先,請注意我們使用了import..as語法。這是一種便利方法,以便于我們可以使用更短的模塊 名稱。在這個例子中,它還讓我們能夠通過簡單地改變一行就切換到另一個模塊(cPickle或者 pickle)!在程序的其余部分的時候,我們簡單地把這個模塊稱為p。
為了在文件里儲存一個對象,首先以寫模式打開一個file對象,然后調用儲存器模塊的dump函 數,把對象儲存到打開的文件中。這個過程稱為 儲存 。
接下來,我們使用pickle模塊的load函數的返回來取回對象。這個過程稱為 取儲存 。
筆記:兩個知識點
1.import..as懶人專用,引用模塊后立即給模塊一個簡稱,用起來省力氣。
2.pickle模塊的兩個函數p.dump (儲存)p.load(取儲存)。敲一遍就知道怎么回事了,不難。
概括:
我們已經討論了多種類型的輸入/輸出,及文件處理和使用儲存器模塊。
接下來,我們將探索異常的概念。
本來打算在這里更新完最后六章的內容,結果寫完這兩章的部分我就發現已經和上兩次的博客篇幅一樣多了。我認為適當控制博客的篇幅易于后期的編輯整理。因此決定最后六章內容改成分成兩個博客。
在更新完《簡明Python教程》讀書筆記之后會開始編寫爬蟲的實戰部分。
ps:雖然我到現在為止都不太清楚應該怎么著手學習爬蟲,但是我相當期待能夠把剛剛學到的Python運用到實際當中。
原創文章,作者:maru,如若轉載,請注明出處:http://www.www58058.com/72687
明顯的比上次的效果要好多了,代碼縮進也有了,但是建議你把代碼的注釋寫上,還有標題,文字縮進等排版。
看不懂了….