Python數據分析與實踐- 課件 第四章 類與對象_第1頁
Python數據分析與實踐- 課件 第四章 類與對象_第2頁
Python數據分析與實踐- 課件 第四章 類與對象_第3頁
Python數據分析與實踐- 課件 第四章 類與對象_第4頁
Python數據分析與實踐- 課件 第四章 類與對象_第5頁
已閱讀5頁,還剩104頁未讀 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

Python數據分析與實踐12023/10/28第4章類與對象本章學習目標:?深刻理解Python中類、對象的概念,掌握它們的構造和使用?熟練掌握Python面向對象的構造函數和析構函數,以及運算符的重載?理解Python類的繼承和組合?熟練掌握Python異常處理機制和內置異常類?熟練掌握Python自定義異常的方法32023/10/284.1面向對象

面向對象編程的英文全稱為ObjectOrientedProgramming,簡稱OOP,它是一種程序設計思想。OOP把對象作為程序的基本單元,一個對象包含了數據和操作數據的函數。

面向對象的程序設計把計算機程序視為一組對象的集合,而每個對象都可以接收其他對象發(fā)過來的消息,并處理這些消息,計算機程序的執(zhí)行就是一系列消息在各個對象之間傳遞。42023/10/284.1.1面向對象編程

面向對象編程的優(yōu)點是易維護、易復用、易擴展,由于面向對象有封裝、繼承、多態(tài)的特性,可以設計出低耦合的系統(tǒng),使系統(tǒng)更加靈活、更加易于維護。

在Python中所有數據類型都可以視為對象,當然用戶也可以自定義對象。自定義的對象數據類型就是面向對象中的類(Class)的概念。52023/10/284.1.1面向對象編程面向對象示例:Student這種數據類型應該被視為一個類,這個類擁有name和score兩個屬性(Property)。如果要打印一個學生的成績,首先必須創(chuàng)建出這個學生類對應的對象,然后給對象發(fā)一個print_score消息,讓對象自己把自己的數據打印出來。具體如下:

classStudent(object):def__init__(self,name,score):=nameself.score=scoredefprint_score(self):print("%s:%s"%(,self.score))62023/10/284.1.1面向對象編程給對象發(fā)消息實際上就是調用對象對應的關聯函數,一般稱之為對象的方法(Method)。面向對象的程序寫出來就像這樣:>>>bart=Student("BartSimpson",59)l>>>isa=Student("LisaSimpson",87)>>>bart.print_score()>>>lisa.print_score()72023/10/284.1.1面向對象編程

面向對象類(Class)和實例(Instance)的設計思想是從自然界中來的。Class是一種抽象概念,比如這里定義的Student是指學生這個概念,而實例(Instance)是一個個具體的Student,比如BartSimpson和LisaSimpson是兩個具體的Student。

所以,面向對象的設計思想是抽象出Class,根據Class創(chuàng)建Instance。面向對象的抽象程度要比函數高,因為一個Class既包含數據又包含操作數據的方法。82023/10/284.1.2類的抽象與封裝

對象包含數據以及操作這些數據的代碼,一個對象包含的所有數據和代碼可以通過類構成一個用戶定義的數據類型。事實上,對象就是類類型(classtype)的變量,一旦定義了一個類,就可以創(chuàng)建這個類的多個對象,每個對象與一組數據相關,而這組數據的類型在類中定義。因此,一個類就是具有相同類型的對象的抽象,例如杧果、蘋果和橘子都是fruit類的對象。類是用戶定義的數據類型,但是在一個程序設計語言中,它和內建的數據類型行為相同。比如創(chuàng)建一個類對象的語法和創(chuàng)建一個整數對象的語法一模一樣。92023/10/284.1.2類的抽象與封裝

把數據和函數裝在一個單獨的單元(稱為類)的行為稱為封裝。數據封裝是類最典型的特點。數據不能被外界訪問,只能被封裝在同一個類中的函數訪問。這些函數提供了對象數據和程序之間的接口。避免數據被程序直接訪問的概念被稱為“數據隱藏”。102023/10/284.1.2類的抽象與封裝

抽象指僅表現核心的特性而不描述背景細節(jié)的行為。類使用了抽象的概念,并且被定義為一系列抽象的屬性,例如尺寸、重量和價格,以及操作這些屬性的函數。類封裝了將要被創(chuàng)建的對象的所有核心屬性。因為類使用了數據抽象的概念,所以它們被稱為抽象數據類型(ADT)。112023/10/284.1.2類的抽象與封裝

封裝機制將數據和代碼捆綁到一起,避免了外界的干擾和不確定性。它同樣允許創(chuàng)建對象。簡單地說,一個對象就是一個封裝了數據和操作這些數據的代碼的邏輯實體。122023/10/284.1.2類的抽象與封裝

在一個對象內部,某些代碼和(或)某些數據可以是私有的,不能被外界訪問。通過這種方式,對象對內部數據提供了不同級別的保護,以防止程序中無關的部分意外地改變或錯誤地使用了對象的私有部分。132023/10/284.1.2類的抽象與封裝

簡而言之,類封裝了一系列方法,并且可以通過一定的規(guī)則約定方法訪問權限。在Python中沒有public、protected、private之類的訪問權限控制修飾詞,Python通過方法名約定訪問權限。例如:(1)普通名字,表示public。(2)以_前導的名字,從語法上視為public,但約定俗稱的意思是“可以被訪問,但請視為private,不要隨意訪問”。(3)以__前導、以__后綴的名字,特殊屬性,表示public。(4)以__前導、不以__后綴的名字,表示private。142023/10/284.1.2類的抽象與封裝

private名字不能被繼承類引用。private不允許通過實例對象直接訪問,本質上是因為private屬性名被Python解釋器改成類名屬性名了,因此仍然可以通過類名屬性名訪問private屬性,但是不同版本的Python解釋器改造的規(guī)則不一致,通常不建議用戶這樣訪問private屬性,因為代碼不具有可移植性。152023/10/284.1.1類的定義與創(chuàng)建

類(Class)可以將它或多或少地看作是類別或者種類的同義詞。在Python中類用來描述具有相同的屬性和方法的對象的集合。它定義了該集合中每個對象所共有的屬性和方法。使用類幾乎可以模擬任何東西。162023/10/284.1.1類的定義與創(chuàng)建類是一個用戶定義類型,與其他大多數計算機語言一樣,Python使用關鍵字class來定義類。函數的語法格式如下:classclassname:<statement-1>...<statement-n><statement-1>與<statement-n>內可以包含任何有效的Python語句,用來定義類的屬性與方法。4.1.1類的定義與創(chuàng)建下面創(chuàng)建一個簡單的類Cat類。classCat():#一次模擬小貓的簡單嘗試def__init__(self,name,age):#初始化屬性name和ageS=nameSelf.age=agedefsit(self):#模擬小貓被命令下蹲print(.title()+“isnowsitting.”)通過如上代碼,就可以創(chuàng)建一個簡單類Cat,賦予了每只小貓下蹲(sit())的能力。4.2.1類的定義與創(chuàng)建在上一節(jié)的案例的代碼中,已經使用到了一個類中的特殊方法:__init__(self,...)方法,這個方法被稱為構造函數,用來初始化對象(實例),在創(chuàng)建新對象時調用。構造函數屬于每個對象,每個對象都有自己的構造函數。如果用戶未設計構造函數,Python將提供一個默認的構造函數。__init__方法在類的一個對象(實例)被建立時,馬上運行。這個方法可以用來對你的對象做一些你希望的初始化。注意,這個名稱的開始和結尾都是雙下劃線。構造函數的作用有個:一是在內存中為類創(chuàng)建一個對象;二是調用類的初始化方法來初始化對象。4.2.1類的定義與創(chuàng)建

__init__()方法定義成包含3個形參,即self、name、age,形參self必不可少,還必須位于其他形參的前面。那么為什么必須在方法定義中包含形參self呢?這是因為Python在調用這個__init__()方法創(chuàng)建Cat實例時將自動傳入實參self。每個與類相關聯的方法調用都自動傳遞實參self,它是一個指向實例本身的引用,讓實例能夠訪問類中的性和方法。在創(chuàng)建Cat實例時,Python將調用Cat類的方法__init__()。程序將通過實參向Cat()傳遞名字和年齡;self會自動傳遞,因此不需要手動去傳遞它。每當根據Cat類創(chuàng)建實例時都只需要給最后兩個形參(name和age)提供值。4.2.1類的定義與創(chuàng)建

接下來創(chuàng)建一個表示特定小貓的實例。首先可以將類視作有關如何創(chuàng)建實例的說明,即可以理解成Cat類是一系列說明,讓Python知道如何創(chuàng)建一個表示特定小貓的實例。my_cat=Cat("tommy",3)print("mycat”snameis"+my_.title()+".")print("mycatis"+str(my_cat.age)+"yearsold.")4.2.1類的定義與創(chuàng)建

前面創(chuàng)建了一個簡單的Cat類,并在方法__init__()中定義了name和age屬性。如果要訪問實例的屬性,可以使用句點表示法。編寫了如下代碼來訪問my_cat的name屬性的值:my_

句點表示法在Python中很有用,這種語法演示了Python語句如何來獲得屬性的值。4.2.2構造函數

示例代碼中已經使用到類中的一個特殊方法———__init__(self,...),這個方法被稱為構造函數,用來初始化對象(實例),在創(chuàng)建新對象時調用。__init__()方法在類的一個對象(實例)被建立時馬上運行。

這個方法可以用來對用戶的對象做一些用戶希望的初始化。構造函數屬于每個對象,每個對象都有自己的構造函數。如果用戶未設計構造函數,Python將提供一個默認的構造函數。注意,這個名稱的開始和結尾都是雙下畫線。

構造函數的作用有兩個:一是在內存中為類創(chuàng)建一個對象;二是調用類的初始化方法來初始化對象。4.2.2構造函數

類的創(chuàng)建和實例化。classPerson:def__init__(self,name):=namedefsayHi(self):print(“Hello,mynameis”,)p=Person(“python”)p.sayHi()運行結果:Hello,mynameispython4.2.2構造函數

__init__()方法定義為取一個參數name(以及普通的參數self)。在這個__init__()里只是創(chuàng)建一個新的域,也稱為name。注意它們是兩個不同的變量,盡管它們有相同的名字。點號使用戶能夠區(qū)分它們。最重要的是,沒有專門調用__init__()方法,只是在創(chuàng)建一個類的新實例的時候把參數包括在圓括號內跟在類名后面,從而傳遞給__init__()方法。這是這種方法的重要之處。4.2.1類屬性和實例屬性

類中的屬性分為兩種:一是類屬性,二是實例屬性。類屬性是在類中方法之外定義的;實例屬性則是在構造函數__init__中定義的,定義時以self為前綴,只能通過對象名訪問,上一小節(jié)創(chuàng)建的Cat類中的和self.age就是實例屬性。為了更好的講解類屬性,將依據上一小節(jié)中創(chuàng)建的一個簡單類Cat,對其進行類屬性的增加和修改來說明。類屬性的修改和增加都是通過“類名.屬性名”的方式直接進行的。4.3.1類屬性和實例屬性增加類屬性:classCat():#一次模擬小貓的簡單嘗試#增加類屬性Reproduction_way=“taisheng”Song_way=“miaomiao”def_init_(self,name,age):#初始化屬性name和ageS=nameSelf.age=agedef_sit_(self):#模擬小貓被命令下蹲print(.title()+“isnowsitting.”)4.3.1類屬性和實例屬性如上面的代碼所示,增加了兩個類屬性,分別是生育后代的方式“taisheng”,以及叫聲“miaomiao”,這是貓類的共同屬性。4.3.2公有屬性和私有屬性

Python并不直接支持私有方式,而是靠程序員自己把握在外部進行特性修改的時機。畢竟在使用對象前應該知道如何使用。但是,可以用一些小技巧達到私有特性的效果。

如果想要讓方法或者特性變?yōu)樗接械?,即從外部無法進行訪問,只需要在它的名字前面加上雙下劃線即可。具體的講,以__(雙下劃線)開頭的屬性是私有屬性,否則這個屬性就是公有屬性。私有屬性通過對象名.類名__私有成員名進行訪問,不能在類外進行直接訪問。4.3.2公有屬性和私有屬性定義私有屬性classSecret():def__unaccessible(self):print(“Sorry,youcannotaccessible...”)defaccessible(self):Print(“Yes,youcanaccessible,andthesecretis...”)Self.__unaccessible()現在,如在這個例子中展示的,__unaccessible是無法從外界進行訪問的。4.3.2公有屬性和私有屬性

但是從類的內部還是能夠進行訪問的(比如從accessible()進行訪問):s=Secret()s.__unaccessible()運行結果:Yes,youcanaccessible,andthesecretis...Sorry,youcannotaccessible...4.3.2公有屬性和私有屬性

雙下畫線雖然有些奇怪,但看起來像是其他編程語言中的標準的私有方法。事實上真正發(fā)生的事情是不標準的。因為在類的內部定義中,所有以雙下畫線開始的命名都將會被翻譯成前面加單下畫線和類名的形式。例如:Secret._Secret__unaccessible運行結果:unboundmethodSecret.__unaccessible4.3.2公有屬性和私有屬性

總體來說,想要確保其他人不會訪問對象的方法和特性是不可能的,但是像這類的“名稱變化術”就是他們不應該訪問這些方法或者特性的強信號。

如果不想使用這種方法,但是又想讓其他對象不能訪問內部數據,那么可以使用雙下畫線。雖然這不過是一個習慣,但的確有實際效果。例如,前面有下畫線的名字都不會被帶星號的import語句“formmoduleimport*”導入。

有些編程語言支持多種層次的成員變量或特性私有化,比如在Java中就支持4種級別。盡管單、雙下畫線在某種程度上給出了兩個級別的私有性,但是Python并沒有真正的私有化支持。4.4.1調用類的方法

在這里,仍舊使用4.2.1小節(jié)中創(chuàng)建的Cat類實例。在創(chuàng)建Cat類實例后,就可以使用句點表示法來調用Cat類中定義的任何方法。

如果想要調用類中方法,可通過指定實例的名稱(這里是my_cat)和想要調用的方法,并用句點分隔它們。遇到代碼my_cat._sit_()時,Python在類Cat中查找方法sit()并運行其代碼塊。classCat():==snip==my_cat=Cat(“tommy”,3)my_cat._sit_()4.4.2類的方法分類

在Python中類的方法大致上可以分為三類:類方法、實例方法和靜態(tài)方法。

類方法,是類對象所擁有的方法,需要用修飾器“@classmethod”來標識其為類方法。它能夠通過實例對象和類對象去訪問。類方法的用途就是可以對類屬性進行修改。對于類方法,第一個參數必須是類對象,一般以“cls”作為第一個參數。舉例如下,classpeople:country="china"@classmethoddefgetCountry(cls):#類方法returncls.country4.4.2類的方法分類

實例方法,在類中最常定義的成員方法,它至少有一個參數并且必須以實例對象作為其第一個參數,一般以名為’self’的變量作為第一個參數。(注意:不能通過類對象引用實例方法)。舉例如下,@classmethoddefsetCountry(cls,country):#類方法cls.country=countryclassInstanceMethod(object):def__init__(self,a):self.a=adeff1(self):print(“Thisis{0}”.format(self))deff2(self,a):print(“Value:{0}”.format(a))4.4.2類的方法分類

靜態(tài)方法,需要通過修飾器”@staticmethod”來進行修飾,靜態(tài)方法對參數沒有要求,不需要多定義參數。在靜態(tài)方法中只能訪問屬于類的成員,不能訪問屬于對象的成員,而靜態(tài)方法也只能通過類名調用。舉例如下,country=“china”@staticmethoddefgetcountry():returnpeople.country@staticmethoddefsetcountry(countryName):people.country=countryName4.4.2類的方法分類

對于這三種不同的方法,就出現了一個問題,既然有了實例方法,類方法和靜態(tài)方法與之相比又有什么好處呢?具體的將,在類方法中,不管是使用實例還是類調用方法,都會把類作為第一個參數傳遞進來,這個參數就是類本身。如果繼承了這個使用類方法的類,該類的所有子類都會擁有了這個方法,并且這個方法會自動指向子類本身。靜態(tài)方法是和類與實例都沒有關系的,完全可以使用一般方法代替,但是使用靜態(tài)方法可以更好的組織代碼,防止代碼變大后變得比較混亂。類方法是可以替代靜態(tài)方法的。靜態(tài)方法不能在繼承中修改。4.4.3析構函數

Python中沒有專用的構造和析構函數,但是一般可以在__init__和__del__分別完成初始化和刪除操作,可用這個替代構造和析構。從這個意義上講,__init__方法:屬于python語言的構造函數;__del__方法:屬于python語言的析構函數,它在對象消逝的時候被調用,用來釋放對象占用的資源。析構函數在對象就要被垃圾回收之前調用,但發(fā)生調用的具體時間是不可知的。4.3.3析構函數將通過一個例子來說明Python的析構函數,舉例如下,classtest():def__init__(self):print(“AAA”)def__del__(self):print(“BBB”)defmy(self):print(“CCC”)>>>obj=test()AAABBB>>>obj.my()CCC>>>delobjBBB4.3.3析構函數

上述例子中的__del__函數就是一個析構函數了,當使用del刪除對象時,會調用他本身的析構函數,另外當對象在某個作用域中調用完畢,在跳出其作用域的同時析構函數也會被調用一次,這樣可以用來釋放內存空間。__del__()也是可選的,如果不提供,則Python會在后臺提供默認析構函數如果要顯式的調用析構函數,可以使用del關鍵字,方式如下:

del對象名412023/10/284.5類的繼承

代碼重用是軟件工程的重要目標之一,類的重用是面向對象的核心內容之一在編寫類時,并非總是要從新開始。如果你要編寫的類是另一個現成類的特殊版本,可使用繼承,在這個現成類的基礎上創(chuàng)建新類,在所創(chuàng)建的新的類中通過添加代碼,來擴展現成類的屬性和方法。這樣不僅能夠減少工作量,而且能降低出現錯誤的可能性。422023/10/284.5.1父類與子類

一個類繼承另一個類時,它將自動獲得另一個類的所有屬性和方法,原有的類稱為基類、父類或超類(Baseclass、Superclass),而新類稱為子類(Subclass)。子類繼承了其父類的所有屬性和方法,同時還可以定義自己的屬性和方法。

在繼承關系中,繼承者是被繼承者的子類。子類繼承所有祖先的非私有屬性和非私有方法,子類也可以增加的屬性和方法,子類還可以通過重定義覆蓋從父類中繼承而來的方法。432023/10/284.5.2繼承的語法具體的繼承語法,通過一個實例來進行展示說明,具體如下。例如,我們已經編寫了一個名為Animal的class,有一個run()方法可以直接打印,classAnimal(object):defrun(self):print(“Animalisrunning...”)442023/10/284.5.2繼承的語法當我們需要編寫Dog和Cat類時,就可以直接從Animal類繼承:classDog(Animal):passclassCat(Animal):pass

此時,對于Dog來說,Animal就是它的父類,對于Animal來說,Dog就是它的子類。Cat和Dog類似。創(chuàng)建子類時,父類必須包含在當前文件中,且位于子類前面。我們定義了子類Dog和Cat。定義子類時,必須在括號內指定父類的名稱。452023/10/284.5.2繼承的語法

繼承有什么好處?最大的好處是子類獲得了父類的全部功能。由于Animial實現了run()方法,因此,Dog和Cat作為它的子類,什么事也沒干,就自動擁有了run()方法:dog=Dog()dog.run()cat=Cat()cat.run()運行結果如下:Animalisrunning...Animalisrunning...462023/10/284.5.2繼承的語法

繼承的第二個好處需要用戶對代碼做一點改進。大家可以看到,無論是Dog還是Cat,它們在run()的時候顯示的都是Animalisrunning...,符合邏輯的做法是分別顯示Dogisrunning...和Catisrunning...,因此對Dog和Cat類改進如下:classDog(Animal):defrun(self):print("Dogisrunning...")classCat(Animal):defrun(self):print("Catisrunning...")運行結果如下:Dogisrunning...Catisrunning...472023/10/284.5.2繼承的語法

當子類和父類存在相同的run()方法時,子類的run()覆蓋父類的run(),在代碼運行時總是會調用子類的run()。當然,用戶也可以給子類增加一些方法,比如Dog類:classDog(Animal):defrun(self):print("Dogisrunning...")defeat(self):print("Eatingmeat...")482023/10/284.5.3多重繼承

繼承是面向對象編程的一個重要的方式,因為通過繼承,子類就可以擴展父類的功能。以一個例子來進行講解。492023/10/284.5.3多重繼承

這里想一下Animal類層次的設計,假設要實現Dog(狗)、Bat(蝙蝠)、Parrot(鸚鵡)、Ostrich(鴕鳥)4種動物,如果按照哺乳動物和鳥類歸類,可以設計出如下類層次。?哺乳類:能跑的哺乳類,能飛的哺乳類。?鳥類:能跑的鳥類,能飛的鳥類。如果要再增加“寵物類”和“非寵物類”,那么類的數量會呈指數增長,很明顯這樣設計是不行的,正確的做法是采用多重繼承。首先,主要的類層次仍按照哺乳類和鳥類設計。502023/10/284.5.3多重繼承classAnimal(object):pass#大類classMammal(Animal):passclassBird(Animal):pass#各種動物classDog(Mammal):passclassBat(Mammal):passclassParrot(Bird):passclassOstrich(Bird):pass512023/10/284.5.3多重繼承

現在,要給動物再加上Runnable和Flyable的功能,只需要先定義好Runnable和Flyable的類:classRunnable(object):defrun(self):print(“Running...”)classFlyable(object):deffly(self):print(“Flying...”)522023/10/284.5.3多重繼承對于需要Runnable功能的動物,就多繼承一個Runnable,例如Dog:classDog(Mammal,Runnable):pass532023/10/284.5.3多重繼承對于需要Flyable功能的動物,就多繼承一個Flyable,例如Bat:classBat(Mammal,Flyable):pass通過多重繼承,一個子類就可以同時獲得多個父類的所有功能。542023/10/284.5.4運算符的重載

在Python類中可以重寫某些運算符的方法函數,例如類中提供了__add__()這個鉤子函數,當調用“+”(加法)運算時,實際上是調用了__add__()鉤子函數,用戶在類中可以重寫這些鉤子函數。552023/10/284.5.4運算符的重載

在Python中帶有前/后綴、雙下畫線的方法函數稱為鉤子函數,鉤子函數具有以下特征:(1)多數鉤子函數均可在類中被重寫。(2)鉤子函數無預設值。(3)相應運算符調用時會自動映射調用這些鉤子函數。562023/10/284.5.4運算符的重載下面表例舉一些常見運算符重載方法:methodoverloadcall__init__構造函數對象創(chuàng)建:X=Class(args)__del__析構函數X對象收回__add__運算法+X+Y,X+=Y__or__運算符|X|Y,X|=Y_repr__,__str__打印,轉換print(X),repr(X),str(X)__call__函數調用X(*args,**kwargs)__getattr__點號運算X.undefined__setattr__屬性賦值語句X.any=value572023/10/284.5.4運算符的重載__delattr__屬性刪除delX.any__getattribute__屬性獲取X.any__getitem__索引運算X[key],X[i:j]__setitem__索引賦值語句X[key],X[i:j]=sequence__delitem__索引和分片刪除delX[key],delX[i:j]__len__長度len(X)__bool__布爾測試bool(X)__lt__,__gt__,__le__,__ge__,__eq__,__ne__特定的比較X<Y,X>Y,X<=Y,X>=Y,X==Y,X!=Y582023/10/284.5.4運算符的重載__lt__,__gt__,__le__,__ge__,__eq__,__ne__特定的比較X<Y,X>Y,X<=Y,X>=Y,X==Y,X!=Y__radd__右側加法other+X__iadd__實地(增強的)加法X+=Y(orelse__add__)__iter__,__next__迭代環(huán)境I=iter(X),next()__contains__成員關系測試iteminX(任何可迭代)__index__整數值hex(X),bin(X),oct(X)__enter__,__exit__環(huán)境管理器withobjasvar:__get__,__set__,__delete__描述符屬性X.attr,X.attr=value,delX.attr__new__創(chuàng)建在__init__之前創(chuàng)建對象592023/10/284.6類的組合

前面講了面向類與對象的繼承,知道了繼承是一種什么“是”什么的關系。然而類與類之間還有另一種關系,這就是組合。這是類的另一種重用的方式,如果程序中的類需要使用一個其他對象,就可以使用類的組合方式。在Python中,一個類可以包含其他類的對象作為屬性,這就是類的組合。

先來看兩個例子:先定義兩個類,一個老師類,老師類有名字,年齡,出生的年,月和日,所教的課程等特征以及走路,教書的技能:602023/10/284.6類的組合classTeacher:def__init__(self,name,age,year,mon,day):=nameself.age=ageself.year=yearself.mon=monself.day=daydefwalk(self):print(“%siswalkingslowly”%)defteach(self):print(“%sisteaching”%)612023/10/284.6類的組合

再定義一個學生類,學生類有名字,年齡,出生的年,月和日,學習的組名等特征以及走路,學習的技能:622023/10/284.6類的組合classStudent:def__init__(self,name,age,year,mon,day):=nameself.age=ageself.year=yearself.mon=monself.day=daydefwalk(self):print(“%siswalkingslowly”%)defstudy(self):print(“%sisstudying”%)632023/10/284.6類的組合

根據類的繼承這個特性,可以把代碼縮減一下。定義一個人類,然后再讓老師類和學生類繼承人類的特征和技能:classPeople:def__init__(self,name,age,year,mon,day):=nameself.age=ageself.year=yearself.mon=monself.day=daydefwalk(self):print(“%siswalking”%)642023/10/284.6類的組合classTeacher(People):def__init__(self,name,age,year,mon,day,course):People.__init__(self,name,age,year,mon,day)self.course=coursedefteach(self):print(“%sisteaching”%)classStudent(People):def__init__(self,name,age,year,mon,day,group):People.__init__(self,name,age,year,mon,day)self.group=groupdefstudy(self):print(“%sisstudying”%)652023/10/284.6類的組合再對老師和學生進行實例化,得到一個老師和一個學生。t1=Teacher(“alex”,28,1989,9,2,“python”)s1=Student(“jack”,22,1995,2,8,“group2”)662023/10/284.6類的組合

現在想知道t1和s1的名字、年齡、出生的年/月/日都很容易,但是想一次性打印出t1或s1的生日就不那么容易了,這時需要用字符串進行拼接,有沒有什么更好的辦法呢?

有,那就是組合。繼承是一個子類與一個父類的關系,而組合是一個類與另一個類的關系。可以說每個人都有生日,而不能說人是生日,這樣就要使用組合的功能。672023/10/284.6類的組合

可以把出生的年/月/日再另外定義一個日期的類,然后用老師或者學生類與這個日期的類組合起來,就可以很容易地得出老師t1或者學生s1的生日,再也不用字符串拼接那么麻煩。請看下面的代碼:682023/10/284.6類的組合classDate:def__init__(self,year,mon,day):self.year=yearself.mon=monself.day=daydefbirth_info(self):print(“Thebirthis%s-%s-%s”%(self.year,self.mon,self.day))692023/10/284.6類的組合classPeople:def__init__(self,name,age,year,mon,day):=nameself.age=ageself.birth=Date(year,mon,day)defwalk(self):print(“%siswalking”%)702023/10/284.6類的組合classTeacher(People):def__init__(self,name,age,year,mon,day,course):People.__init__(self,name,age,year,mon,day)self.course=coursedefteach(self):print(“%sisteaching”%)712023/10/284.6類的組合classStudent(People):def__init__(self,name,age,year,mon,day,group):People.__init__(self,name,age,year,mon,day)self.group=groupdefstudy(self):print(“%sisstudying”%)t1=Teacher(“alex",28,1989,9,2,"python”)s1=Student(“jack",22,1995,2,8,"group2”)722023/10/284.6類的組合

這樣一來,可以使用跟前面一樣的方法來調用老師t1或學生s1的姓名,年齡等特征以及走路,教書或者學習的技能。print()t1.walk()t1.teach()輸出為:alexalexiswalkingalexisteaching732023/10/284.6類的組合

那要怎么能夠知道他們的生日呢:print(t1.birth)輸出為:<__main__.Dateobjectat0x0000000002969550>742023/10/284.6類的組合

這個birth是子類Teacher從父類People繼承過來的,而父類People的birth又是與Date這個類組合在一起的,所以,這個birth是一個對象。而在Date類下面有一個birth_info的技能,這樣就可以通過調用Date下面的birth_info這個函數屬性來知道老師t1的生日了。t1.birth.birth_info()得到的結果為:Thebirthis1989-9-2752023/10/284.6類的組合

組合就是一個類中使用到另一個類,從而把幾個類拼到一起。組合的功能也是為了減少重復代碼。

在實際的項目開發(fā)過程中,如果僅僅是只使用繼承和組合中的一種技術,是很難滿足實際需求的,所以在實際的開發(fā)過程中,開發(fā)人員通常會將兩種技術結合起來使用。762023/10/284.7類的異常處理異常處理在任何一門編程語言里都是非常被關注的一個話題,良好的異常處理可以讓程序更加健壯,清晰的錯誤信息更能幫助快速修復問題。在Python中,和部分高級語言一樣,使用了try/except語句塊來處理異常,如果你有其他編程語言的經驗,實踐起來并不難。772023/10/284.7.1異常

異常即是在程序執(zhí)行過程中發(fā)生的影響程序正常運行的一個事件,該事件會在程序執(zhí)行過程中發(fā)生,影響了程序的正常執(zhí)行。一般情況下,在Python無法正常處理程序時就會發(fā)生一個異常。異常是Python對象,表示一個錯誤。當Python腳本發(fā)生異常時我們需要捕獲處理它,否則程序會終止執(zhí)行。異常處理使程序能夠處理完異常后繼續(xù)它的正常執(zhí)行,不至于使程序因異常導致退出或崩潰。782023/10/284.7.1異常舉一個具體的例子:打開一個不存在的文件。代碼如下:fr=open(“/notthere”,”r”)運行結果:Traceback(mostrecentcalllast):File“tiaoshi005.py”,line1,in<module>fr=open(“/notthere”,”r”)FileNotFoundError:[Errno2]Nosuchfileordirectory:‘/notthere’例子中的代碼視圖打開一個不存在的文件,運行之后,拋FileNotFoundError異常。4.7.2Python中的異常類

Python程序出現異常時將拋出一個異常類的現象。Python中所有的異常類的根類都是BaseException類,他們都是BaseException的直接或間接子類。大部分常規(guī)異常類的基類是Exception的子類。下表列出了Python中內置的標準異常。而自定義異常類都是繼承自這些標準異常。異常名稱描述BaseException所有異常的基類SystemExit解釋器請求退出KeyboardInterrupt用戶中斷執(zhí)行(通常是輸入^C)Exception常規(guī)錯誤的基類StopIteration迭代器沒有更多的值4.7.2Python中的異常類GeneratorExit生成器(generator)發(fā)生異常來通知退出StandardError所有的內建標準異常的基類ArithmeticError所有數值計算錯誤的基類FloatingPointError浮點計算錯誤OverflowError數值運算超出最大限制ZeroDivisionError除(或取模)零(所有數據類型)AssertionError斷言語句失敗AttributeError對象沒有這個屬性4.7.2Python中的異常類EOFError沒有內建輸入,到達EOF標記EnvironmentError操作系統(tǒng)錯誤的基類IOError輸入/輸出操作失敗OSError操作系統(tǒng)錯誤WindowsError系統(tǒng)調用失敗ImportError導入模塊/對象失敗LookupError無效數據查詢的基類IndexError序列中沒有此索引(index)4.7.2Python中的異常類KeyError映射中沒有這個鍵MemoryError內存溢出錯誤(對于Python解釋器不是致命的)NameError未聲明/初始化對象(沒有屬性)UnboundLocalError訪問未初始化的本地變量ReferenceError弱引用(Weakreference)試圖訪問已經垃圾回收了的對象RuntimeError一般的運行時錯誤NotImplementedError尚未實現的方法SyntaxErrorPython語法錯誤4.7.2Python中的異常類IndentationError縮進錯誤TabErrorTab和空格混用SystemError一般的解釋器系統(tǒng)錯誤TypeError對類型無效的操作ValueError傳入無效的參數UnicodeErrorUnicode相關的錯誤UnicodeDecodeErrorUnicode解碼時的錯誤UnicodeEncodeErrorUnicode編碼時錯誤4.7.2Python中的異常類UnicodeTranslateErrorUnicode轉換時錯誤Warning警告的基類DeprecationWarning關于被棄用的特征的警告FutureWarning關于構造將來語義會有改變的警告OverflowWarning舊的關于自動提升為長整型(long)的警告PendingDeprecationWarning關于特性將會被廢棄的警告RuntimeWarning可疑的運行時行為(runtimebehavior)的警告SyntaxWarning可疑的語法的警告UserWarning用戶代碼生成的警告4.7.3捕獲與處理異常捕捉異常通常使用try/except語句。

try/except語句用來檢測try語句塊中的錯誤,從而讓except語句捕獲異常信息并處理。如果不想在異常發(fā)生時結束程序,只需在try里捕獲它。4.7.3捕獲與處理異常以下為簡單的try....except的語法:try:<語句>#運行別的代碼except<名字>:<語句>#如果在try部份引發(fā)了'name'異常except<名字>,<數據>:<語句>#如果引發(fā)了'name'異常,獲得附加的數據4.7.3捕獲與處理異常

當開始一個try語句后,Python就在當前程序的上下文中作標記,這樣當異常出現時就可以回到這里,try子句先執(zhí)行,接下來會發(fā)生什么依賴于執(zhí)行時是否出現異常。

如果當try后的語句執(zhí)行時發(fā)生異常,Python就跳回到try并執(zhí)行第一個匹配該異常的except子句,異常處理完畢,控制流就通過整個try語句(除非在處理異常時又引發(fā)新的異常)。

如果在try后的語句里發(fā)生了異常,卻沒有匹配的except子句,異常將被遞交到上層的try,或者到程序的最上層(這樣將結束程序,并打印缺省的出錯信息)。如果在try子句執(zhí)行時沒有發(fā)生異常,Python將執(zhí)行else語句后的語句(如果有else的話),然后控制流通過整個try語句。4.7.3捕獲與處理異常當然也可以不帶任何異常類型使用except,如下示例:try:正常的操作......................except:發(fā)生異常,執(zhí)行這塊代碼......................else:如果沒有異常執(zhí)行這塊代碼以上方式try-except語句捕獲所有發(fā)生的異常。但這不是一個很好的方式,我們不能通過該程序識別出具體的異常信息,因為它捕獲所有的異常。4.7.4自定義異常類

通過創(chuàng)建一個新的異常類,程序可以命名自己的異常。自定義異常應該是通過直接或間接的方式繼承自典型的Exception類。4.7.4自定義異常類

以下為與RuntimeError相關的實例,實例中創(chuàng)建了一個類,基類為RuntimeError,用于在異常觸發(fā)時輸出更多的信息。在try語句塊中,用戶自定義的異常后執(zhí)行except塊語句,變量e是用于創(chuàng)建Networkerror類的實例。classNetworkerror(RuntimeError): def__init__(self,arg): self.args=arg4.7.4自定義異常類在定義以上類后,可以觸發(fā)該異常,如下所示:try: raiseNetworkerror("Badhostname")except(Networkerror)ase: print(e.args)但是,因為Networkerror是一個自定義類,因此需要使用raise來顯式地拋出異常。4.7.4自定義異常類

自定義異常的其他使用方法則與標準模塊中的異常類的使用方法一致。下面將通過一個具體的例子來進行自定義異常使用的詳細講解。具體如下:classShortInputException(Exception):#Auser-definedexceptionclass.def__init__(self,length,atleast):Exception.__init__(self)self.length=length self.atleast=atleast4.7.4自定義異常類try:s=raw_input('Entersomething-->')iflen(s)<3:raiseShortInputException(len(s),3)else:print(s)exceptEOFError:print'\nWhydidyoudoanEOFonme?'exceptShortInputExceptionasx:print('ShortInputException:Theinputwaslength%d,\wasexpectingatleast%d.'%(x.length,x.atleast))else:print('Noexceptionwasraised.')4.7.4自定義異常類

在上述例子中,先自定義了一個名為ShortInputException的異常類,其用來判斷用戶輸入的字符串長度是否滿足要求。在本例子中,其判斷輸入的字符串長度是否等于大于3個字符,若不滿足,則拋出該異常。4.7.5with語句

有一些任務,可能事先需要設置,事后做清理工作。對于這種場景,Python的with語句提供了一種非常方便的處理方式。一個很好的例子是文件處理,你需要獲取一個文件句柄,從文件中讀取數據,然后關閉文件句柄。

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論