摘要:新的稱為子類,而被繼承的稱為基類父類或超類。繼承最大的好處是子類獲得了父類的全部功能。在繼承關系中,如果一個實例的數(shù)據(jù)類型是某個子類,那它的數(shù)據(jù)類型也可以被看做是父類。
在上一篇中我們介紹了模塊和數(shù)據(jù)結構,這一篇將介紹面向對象編程。
面向對象編程——Object Oriented Programming,簡稱 OOP,是一種程序設計思想。OOP 把對象作為程序的基本單元,一個對象包含了數(shù)據(jù)和操作數(shù)據(jù)的函數(shù)。它將數(shù)據(jù)與功能進行組合,并將其包裝在被稱作“對象”的東西內。
在Python中,所有數(shù)據(jù)類型都可以視為對象,也可以自定義對象。一個類(Class)能夠創(chuàng)建一種新的類型(Type),其中對象(Object)就是類的實例(Instance)。
可以這樣來類比:你可以擁有類型 int 的變量,也就是說存儲整數(shù)的變量是 int 類的實例(對象)。
面向對象的設計思想是抽象出Class,根據(jù)Class創(chuàng)建Instance。
給對象發(fā)消息實際上就是調用對象對應的關聯(lián)函數(shù),我們稱之為對象的方法(Method)。
數(shù)據(jù)封裝、繼承和多態(tài)是面向對象的三大特點。
類和實例面向對象最重要的概念就是類(Class)和實例(Instance)。
類:是抽象的模板,比如 Student 類。
實例:是根據(jù)類創(chuàng)建出來的一個個具體的“對象”,每個對象都擁有相同的方法,但各自的數(shù)據(jù)可能不同。
在 Python 中,定義類是通過class關鍵字。class后面緊接著是類名,類名通常是大寫開頭的單詞。緊接著是object,表示該類是從哪個類繼承下來的。通常,如果沒有合適的繼承類,就使用object 類,這是所有類最終都會繼承的類。
創(chuàng)建實例是通過類名加()實現(xiàn)的:
變量bart指向的就是一個Student的實例,后面的0x1032d3470是內存地址,每個object的地址都不一樣,而Student本身則是一個類。
可以自由地給一個實例變量綁定屬性,比如,給實例bart綁定一個name屬性。
在創(chuàng)建實例的時候,把一些我們認為必須綁定的屬性強制填寫進去。通過定義一個特殊的__init__方法,在創(chuàng)建實例的時候,就把name,score等屬性綁上去:
注意_init__前后分別有兩個下劃線。
__init__方法的第一個參數(shù)永遠是self,表示創(chuàng)建的實例本身,因此,在__init__方法內部,就可以把各種屬性綁定到self,因為self就指向創(chuàng)建的實例本身。
有了__init__方法,在創(chuàng)建實例的時候,就不能傳入空的參數(shù)了,必須傳入與__init__方法匹配的參數(shù),但self不需要傳,Python 解釋器自己會把實例變量傳進去。
和普通的函數(shù)相比,在類中定義的函數(shù)只有一點不同,就是第一個參數(shù)永遠是實例變量self,并且,調用時不用傳遞該參數(shù)。除此之外,類的方法和普通函數(shù)沒有什么區(qū)別,仍然可以用默認參數(shù)、可變參數(shù)、關鍵字參數(shù)和命名關鍵字參數(shù)。
數(shù)據(jù)封裝在上面的Student類中,每個實例就擁有各自的name和score這些數(shù)據(jù)。我們可以通過函數(shù)來訪問這些數(shù)據(jù),比如打印一個學生的成績:
既然Student實例本身就擁有這些數(shù)據(jù),要訪問這些數(shù)據(jù),就沒有必要從外面的函數(shù)去訪問,可以直接在Student類的內部定義訪問數(shù)據(jù)的函數(shù),這樣,就把“數(shù)據(jù)”給封裝起來了。、
這些封裝數(shù)據(jù)的函數(shù)是和Student類本身是關聯(lián)起來的,我們稱之為類的方法:
要定義一個方法,除了第一個參數(shù)是self外,其他和普通函數(shù)一樣。要調用一個方法,只需要在實例變量上直接調用,除了self不用傳遞,其他參數(shù)正常傳入:
這樣,我們從外部看Student類,就只需要知道創(chuàng)建實例需要給出name和score,而如何打印,都是在Student類的內部定義的,這些數(shù)據(jù)和邏輯被“封裝”起來了,調用很容易,但卻不用知道內部實現(xiàn)的細節(jié)。
類是創(chuàng)建實例的模板,而實例則是一個一個具體的對象,各個實例擁有的數(shù)據(jù)都互相獨立,互不影響;
方法就是與實例綁定的函數(shù),和普通函數(shù)不同,方法可以直接訪問實例的數(shù)據(jù);
通過在實例上調用方法,我們就直接操作了對象內部的數(shù)據(jù),但無需知道方法內部的實現(xiàn)細節(jié)。
和靜態(tài)語言不同,Python 允許對實例變量綁定任何數(shù)據(jù),也就是說,對于兩個實例變量,雖然它們都是同一個類的不同實例,但擁有的變量名稱都可能不同。
繼承和多態(tài)在 OOP 程序設計中,當我們定義一個class的時候,可以從某個現(xiàn)有的class繼承。新的class稱為子類(Subclass),而被繼承的class稱為基類、父類或超類(Base class、Super class)。
繼承最大的好處是:子類獲得了父類的全部功能。
也可以對子類增加一些方法。
當子類和父類都存在相同的run()方法時,我們說,子類的run()覆蓋了父類的run(),在代碼運行的時候,總是會調用子類的run()。
這樣,我們就獲得了繼承的另一個好處:多態(tài)。
當我們定義一個class的時候,我們實際上就定義了一種數(shù)據(jù)類型。我們定義的數(shù)據(jù)類型和 Python 自帶的數(shù)據(jù)類型,比如str、list、dict沒什么兩樣。在繼承關系中,如果一個實例的數(shù)據(jù)類型是某個子類,那它的數(shù)據(jù)類型也可以被看做是父類。但是,反過來就不行。
新增一個Animal的子類,不必對run_twice()做任何修改。實際上,任何依賴Animal作為參數(shù)的函數(shù)或者方法都可以不加修改地正常運行,原因就在于多態(tài)。
多態(tài)的意思:
對于一個變量,我們只需要知道它是Animal類型,無需確切地知道它的子類型,就可以放心地調用run()方法,而具體調用的run()方法是作用在Animal、Dog、Cat還是Tortoise對象上,由運行時該對象的確切類型決定。
這就是多態(tài)真正的威力,調用方只管調用,不管細節(jié),而當我們新增一種Animal的子類時,只要確保run()方法編寫正確,不用管原來的代碼是如何調用的。這就是著名的“開閉”原則:
對擴展開放:允許新增 Animal 子類;
對修改封閉:不需要修改依賴 Animal 類型的 run_twice( ) 等函數(shù)。
任何類,最終都可以追溯到根類object,這些繼承關系看上去就像一顆倒著的樹。比如如下的繼承樹:
對于靜態(tài)語言(例如 Java)來說,如果需要傳入Animal類型,則傳入的對象必須是Animal類型或者它的子類,否則,將無法調用run()方法。
對于 Python 這樣的動態(tài)語言來說,則不一定需要傳入Animal類型。我們只需要保證傳入的對象有一個run()方法就可以了。
這就是動態(tài)語言的“鴨子類型”,它并不要求嚴格的繼承體系,一個對象只要“看起來像鴨子,走起路來像鴨子”,那它就可以被看做是鴨子。
異常在程序運行過程中,總會遇到各種各樣的錯誤。
程序編寫有問題造成的:比如本來應該輸出整數(shù)結果輸出了字符串。這種錯誤我們通常稱之為 bug,bug 是必須修復的。
用戶輸入造成的:比如讓用戶輸入 email 地址,結果得到一個空字符串。這種錯誤可以通過檢查用戶輸入來做相應的處理。
完全無法在程序運行過程中預測的:比如寫入文件的時候,磁盤滿了,寫不進去了;或者從網絡抓取數(shù)據(jù),網絡突然斷掉了。這類錯誤也稱為異常,在程序中通常是必須處理的,否則,程序會因為各種問題終止并退出。
Python 內置了一套異常處理機制,來幫助我們進行錯誤處理。
調試:跟蹤程序的執(zhí)行,查看變量的值是否正確。Python 的 pdb 可以讓我們以單步方式執(zhí)行代碼。
編寫測試:有了良好的測試,就可以在程序修改后反復運行,確保程序輸出符合我們編寫的測試。
錯誤處理
高級語言通常都內置了一套try...except...finally...的錯誤處理機制
try的機制:
try: print("try...") r = 10 / 0 print("result:", r) except ZeroDivisionError as e: print("except:", e) finally: print("finally...") print("END")
當我們認為某些代碼可能會出錯時,就可以用try來運行這段代碼,如果執(zhí)行出錯,則后續(xù)代碼不會繼續(xù)執(zhí)行,而是直接跳轉至錯誤處理代碼,即except語句塊,執(zhí)行完except后,如果有finally語句塊,則執(zhí)行finally語句塊,至此,執(zhí)行完畢。
可以有多個except來捕獲不同類型的錯誤。如果沒有錯誤發(fā)生,可以在except語句塊后面加一個else,當沒有錯誤發(fā)生時,會自動執(zhí)行else語句。
可以跨越多層調用,比如函數(shù)main()調用foo(),foo()調用bar(),結果bar()出錯了,這時,只要main()捕獲到了,就可以處理。也就是說,不需要在每個可能出錯的地方去捕獲錯誤,只要在合適的層次去捕獲錯誤就可以了。
Python 所有的錯誤都是從BaseException類派生的,點擊可查看常見的錯誤類型和繼承關系。
如果錯誤沒有被捕獲,它就會一直往上拋,最后被 Python 解釋器捕獲,打印一個錯誤信息,然后程序退出。
錯誤信息第 1 行:
Traceback (most recent call last):
告訴我們這是錯誤的跟蹤信息。
依次往下看,根據(jù)錯誤類型,判斷最后的錯誤源頭。
出錯的時候,一定要分析錯誤的調用棧信息,才能定位錯誤的位置。
Python 內置的logging模塊可以非常容易地記錄錯誤信息。同樣是出錯,但程序打印完錯誤信息后會繼續(xù)執(zhí)行,并正常退出。通過配置,logging還可以把錯誤記錄到日志文件里,方便事后排查。
更多更多關于 Python 的學習可閱讀Python 官方文檔,Python 標準庫文檔,Python的第三方庫。
進一步探索標準庫的一個好方法是閱讀由 Doug Hellmann 撰寫的優(yōu)秀的 Python Module of the Week 系列。
參考鏈接:
簡明Python教程(電子書可閱讀)
廖雪峰Python教程
如有不足,歡迎指正。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://systransis.cn/yun/42187.html
摘要:在定義函數(shù)時給定的名稱稱作形參,在調用函數(shù)時你所提供給函數(shù)的值稱作實參。調用函數(shù)要調用一個函數(shù),需要知道函數(shù)的名稱和參數(shù)。默認參數(shù)值可以有效幫助解決這一情況。是默認參數(shù)定義默認參數(shù)要牢記一點默認參數(shù)必須指向不變對象。 關于數(shù)據(jù)科學在做什么,我們已經在前兩篇文章中進行了總結,即專題概述和描述性統(tǒng)計分析。要進行數(shù)據(jù)科學的探索,需要一個好工具,就是Python。從本篇開始,將總結學習Pyth...
摘要:有一些表示常見圖形的對象稱為塊,完整的集合位于。中的繪圖函數(shù)在中,有行標簽列標簽分組信息。密度圖通過計算可能會產生觀測數(shù)據(jù)的連續(xù)概率分布的估計而產生的。在探索式數(shù)據(jù)分析工作中,同時觀察一組變量的散布圖是很有意義的。 我們在上一篇介紹了 pandas,本篇介紹 matplotlib。 繪圖和可視化 一個用于創(chuàng)建出版質量圖表的桌面繪圖包。 Matplotlib API入門 Figure ...
摘要:提供了使我們能夠快速便捷地處理結構化數(shù)據(jù)的大量數(shù)據(jù)結構和函數(shù)。結構化數(shù)據(jù),例如多維數(shù)據(jù)矩陣表格行數(shù)據(jù),其中各列可能是不同的類型字符串數(shù)值日期等?;A數(shù)組和矢量計算高性能科學計算和數(shù)據(jù)分析的基礎包。 本篇內容為整理《利用Python進行數(shù)據(jù)分析》,博主使用代碼為 Python3,部分內容和書本有出入。 利用 Python 進行科學計算的實用指南。本書重點介紹了用于高效解決各種數(shù)據(jù)分析問...
閱讀 1972·2021-10-25 09:48
閱讀 2800·2021-09-22 14:59
閱讀 1763·2019-08-29 16:52
閱讀 869·2019-08-29 16:07
閱讀 2310·2019-08-29 12:38
閱讀 1766·2019-08-26 13:23
閱讀 886·2019-08-26 11:49
閱讀 3282·2019-08-26 10:56