1.3面向對象
1.3.1面向對象基礎
1.3.1.1面向對象
1.3.1.1.1基本概念
- 過程和函數
- 過程類似於函數,只能執行,但是沒有返回值
- 函數不僅能執行而且有返回結果
- 簡單來說,面向對象就是相對於函數而言更大的封裝,將多個函數封裝給一個對象,每個對象具有不同的職責,在開發時根據不同的職責調用不同的對象就可以極大的簡化開發的效率
1.3.1.1.2類和對象
- 類是一羣具有相同特徵或者行爲的事物的一個統稱,不能直接使用;其中特徵被稱爲屬性,行爲被稱爲方法;簡單來說,類就相當於製造飛機的圖紙,是一個模版是負責創造對象的,類是抽象的是負責創建對象的
- 對象是由類創造出來的一個具體存在,可以直接使用;簡單來說,對象就相當於飛機;所以是先有類纔有對象;對象中就有相應的屬性和方法
- 類和對象的關係:類只有一個,對象可以有多個(可以根據一個圖紙製造多個飛機),同一個類的不同對象可能會有所不同;類中定義了什麼方法和屬性,對象中就有什麼方法和屬性,不可能多也不可能少
1.3.1.1.3類的三要素
- 類名:一般類名要滿足大駝峯命名法(首字母大寫)
- 屬性:這類事物具有什麼特徵(通常用名詞來表示)
- 方法:這類事物具有什麼行爲(通常是動詞來表示)
1.3.1.1.4內置函數dir查詢對象的方法列表
-
在python中對象幾乎是無所不在的,變量、數據、函數都是對象
-
python中兩種查看對象內所有方法和屬性的方法
-
在標識符/數據後輸入一個.然後按下tab鍵,ipython就會提示該對象的所有能夠使用的方法列表
-
使用內置函數dir()傳入標識符/數據,可以查看對象內的所有屬性和方法:其中__方法名__是表示python提供的內置方法/屬性
list_temp = [] print(dir(list_temp))
-
1.3.1.2定義簡單類
1.3.1.2.1基本語法
-
創建類
class 類名: def 方法1(self, 參數列表): # 類中的方法第一個參數必須是self pass def 方法2(self, 參數列表): pass
-
創建對象
對象變量 = 類名()
-
案例
class Cat: def eat(self): print("eat") def drink(self): print("drink") cat = Cat() cat.drink() cat.eat()
-
拓展
-
如果直接使用print打印對象變量,默認情況下會輸入這個變量的引用對象是由哪一個類創建的對象以及在內存中的地址(默認是十六進制表示%x),變量引用的概念同樣適用於對象(即對象變量存儲的是對象在內存中的地址,注意這裏不是類在內存中的地址,即同一個類的不同對象在內存中的地址不同,但如果給對象賦值一個對象則兩個對象的地址相同)
class Cat: def eat(self): print("eat") def drink(self): print("drink") cat = Cat() cat.drink() cat.eat() print(cat) addr = id(cat) # id()函數可以得到變量在內存中的地址 print("%x" % addr) # 用十六進制的方式輸出cat對象在內存中的地址 print("%d" % addr) # 用十進制的方式輸出cat對象在內存中的地址
-
1.3.1.2.2self
-
臨時爲對象創建一個屬性:對象名.屬性名=值,由於這種方法沒有在類中進行修改,所以在實際開發中不推薦使用
cat.name = "貓1" # 這裏就爲cat對象添加了一個屬性name,值爲貓1
-
self:哪一個對象調用了類的方法,self就表示哪一個對象的引用
class Cat: def eat(self): print("%seat" % self.name) # 通過self來調用調用該類的對象的屬性 def drink(self): print("drink") cat = Cat() cat.name = "懶貓" cat.drink() cat.eat()
-
在執行程序時,類和函數一樣不會被執行,只有當調用時纔會被執行
1.3.1.3初始化方法
-
在python中創建一個對象時python會自動做兩件事:爲對象開闢一個空間;調用初始化方法(不需調用該方法在創建對象的同時就直接執行該方法)
class Cat: def __init__(self): # 初始化方法的名字是固定的 print("初始化") def eat(self): print("%seat" % self.name) # 通過self來調用調用該類的對象的屬性 def drink(self): print("drink") cat = Cat() # 在創建對象時,python會自動調用初始化方法 cat.name = "懶貓" cat.drink() cat.eat()
-
如果想要一個類的所有對象在創建的同時就有一些屬性,就需要在類中的初始化方法中定義這類屬性
class Cat: def __init__(self, new_name): # 初始化方法的名字是固定的,通過使用參數來傳遞值 print("初始化") self.name = new_name # 定義所有該類的對象都具有都屬性 def eat(self): print("%s正在eat" % self.name) # 通過self來調用調用該類的對象的屬性 def drink(self): print("drink") cat = Cat("tom") # 在創建對象時,python會自動調用初始化方法並傳遞相關的參數 cat.drink() cat.eat() print(cat.name)
1.3.1.4內置方法
1.3.1.4.1_del_()方法
-
當一個對象在內存中被銷燬前都會自動調用_del_()方法
class Cat: def __init__(self): print("come") def __del__(self): print("back") cat = Cat() del cat # del可以銷燬變量,此時自動調用__del__方法 print("*" * 30) # 如果不提前銷燬對象,由於cat是全局變量,這裏在程序執行完畢後纔會被銷燬並自動調用__del__方法
1.3.1.4.2_str_()方法
-
在默認的情況下使用print輸出對象時會自動打印對象所在的類和對象在內存中的地址,如果希望使用print輸出對象時可以輸出自己想要的內容就可以使用_str_()方法
class Cat: def __init__(self, new_name): print("come") self.name = new_name def __del__(self): print("back") def __str__(self): # __str__()方法必須有一個返回值 return "我是%s" % self.name cat = Cat("小紅") print(cat) # 可以自定義輸出內容 del cat # del可以銷燬變量,此時自動調用__del__方法 print("*" * 30) # 如果不提前銷燬對象,由於cat是全局變量,這裏在程序執行完畢後纔會被銷燬並自動調用__del__方法
1.3.2面相對象練習
-
小明愛跑步案例
class Person: def __init__(self, new_name, new_weight): self.name = new_name self.weight = new_weight def __str__(self): return "我的名字是%s,體重是%.2f斤" % (self.name, self.weight) def run(self): print("%s愛跑步" % self.name) self.weight -= 1 def eat(self): print("%s愛喫零食" % self.name) self.weight += 1 person_boy = Person("小明", 103) person_girl = Person("小美", 90) person_boy.run() # self這個參數不需要傳值 print(person_boy) print(person_girl)
-
擺放傢俱案例
# 通常在實際開發中被使用的類要先被開發(如擺放傢俱案例中的傢俱類要比房子類要先定義) # 定義一個傢俱類 class HouseItem: def __init__(self, new_name, new_area): """傢俱的默認屬性 :param new_name: 傢俱的名稱 :param new_area: 傢俱的佔地面積 """ self.name = new_name self.area = new_area def __str__(self): return "%s佔地%.2f" % (self.name, self.area) # 定義一個房子類 class House: def __init__(self, new_house_type, new_area): """房子的默認屬性 :param new_house_type: 房子的戶型 :param new_area: 房子的佔地面積 """ self.house_type = new_house_type self.area = new_area # 剩餘面積 self.free_area = new_area # 對於新房子而言剩餘面積就是初始面積 # 傢俱名稱列表 self.item_list = [] def __str__(self): # python能夠自動將一對括號內部的代碼連接在一起 return ("戶型:%s\n總面積:%.2f【剩餘%.2f】\n傢俱:%s" % (self.house_type, self.area, self.free_area, self.item_list)) def add_item(self, item): """添加傢俱 :param item: 要添加的傢俱名稱 :return: """ print("要添加%s" % item) """如果參數是一個對象則輸出的內容是print(對象),相當於 輸出__str__()函數的內容""" # 判斷傢俱的面積 if item.area > self.free_area: # 在創建傢俱對象的同時就默認有area和name屬性,所以這裏可以直接調用 print("%s面積超出無法添加" % item.name) return # 將傢俱的名稱添加到列表中 self.item_list.append(item.name) # 計算剩餘面積 self.free_area -= item.area # 添加傢俱 bed = HouseItem("席夢思", 4) chest = HouseItem("衣櫃", 2) table = HouseItem("餐桌", 1.5) print(bed) print(chest) print(table) # 創建房子對象 my_home = House("兩室一廳", 60) # 通過add_item()方法將傢俱放置到房子中 my_home.add_item(bed) my_home.add_item(chest) my_home.add_item(table) print(my_home)
-
士兵突擊案例
class Gun: # 槍類的定義 def __init__(self, new_model): """ :param new_model: 槍的型號 """ self.model = new_model self.bullet_count = 0 def add_bullet(self, count): """給槍加子彈 :param count: 添加子彈的數量 :return: """ self.bullet_count += count def shoot(self): """發射 :return: """ if self.bullet_count <= 0: print("%s沒有子彈了" % self.model) return else: self.bullet_count -= 1 print("%s發射成功,還剩%d子彈" % (self.model, self.bullet_count)) class Soldier: # 士兵類的定義 def __init__(self, new_name): self.name = new_name # 在定義屬性時如果不知道設置什麼初始值可以設置爲None self.gun = None # 新兵沒有槍,這裏要設置一個對象 def fire(self): """士兵開槍 :return: """ # 判斷士兵是否有槍 if self.gun is None: # python中對None對比較推薦使用is而不是== print("%s還沒有槍" % self.name) return else: print("%s衝啊" % self.name) # 裝填子彈 self.gun.add_bullet(50) # 調用槍對象的方法 # 發射子彈 self.gun.shoot() # 創建槍對象 ak47 = Gun("ak47") # 創建士兵對象 soldier_boy = Soldier("soldier_boy") # 給士兵一把槍 soldier_boy.gun = ak47 # 這裏的ak47是一個對象,就是上面創建的槍對象 # 士兵開火 soldier_boy.fire()
-
身份運算符
- 在python中身份運算符用於比較兩個對象所引用的內存地址是否一致,在python中針對None比較時推薦使用is判斷(is判斷兩個對象所引用的地址是否相同,is not用來判斷兩個對象所引用的地址是否不同)
- is和==的區別:is用來判斷兩個變量所引用的地址是否相同,==用來判斷兩個變量所引用的地址中的數值是否相同
-
私有屬性和方法
-
在實際開發中,某些對象的屬性和方法只希望在對象的內部被使用,而不希望在外部被調用,這些屬性和方法就被稱爲私有屬性和方法
-
定義:在定義私有屬性和私有方法時,在私有屬性和私有方法的名字前面添加兩個下劃線就表示這個屬性和方法就是私有屬性和私有方法
class Women: def __init__(self, new_name, new_age): self.name = new_name self.__age = new_age def __secret(self): print("我的姓名是%s,我的年齡是%d" % (self.name, self.__age)) # 對象的內部可以直接訪問私有屬性和私有方法 girl = Women("xiaofang", 18) girl.__secret() # 對象的外部不可以直訪問對象的私有屬性和私有方法 """個人理解:這裏的對象內部和外部可以理解爲類的內部和外部"""
-
-
僞私有屬性和方法
-
在python中沒有真正意義的私有(其實python的私有屬性和私有方法是僞私有屬性和僞私有方法,其實在對象的外部可以通過特定的方式來調用私有屬性和私有方法)
class Women: def __init__(self, new_name, new_age): self.name = new_name self.__age = new_age def __secret(self): print("我的姓名是%s,我的年齡是%d" % (self.name, self.__age)) girl = Women("xiaofang", 18) # 在對象外部只需在私有方法或私有屬性的前面加上_類名(私有方法或私有屬性所在的類)就可以調用私有方法或私有屬性 girl._Women__secret()
-
如何在外部調用私有屬性和私有方法(在實際開發中不要這麼使用,私有的方法和屬性就不要在外部調用)
-
1.3.3單繼承和方法的重寫
1.3.3.1單繼承
1.3.3.1.1問題的拋出
- 面向對象的三大特性:繼承、封裝、多態
- 繼承:實現代碼的重用,相同的代碼不需要在重複的編寫
- 封裝:根據職責將屬性和方法封裝到一個抽象的類中
- 多態:不同的對象調用相同的方法,產生不同的執行結果,增加代碼的靈活度
1.3.3.1.2繼承的概念和語法
-
繼承的語法
class 類名(父類名): # 子類就具有父類的屬性和方法而不需要重新定義 pass
1.3.3.1.3繼承的相關術語:繼承和派生
- 如果一個Dog類繼承Animal類,那麼可以說Dog類是Animal類的子類,Animal類是Dog類的父類,Dog類從Animal類繼承;也可以說Dog類是Animal類的派生類,Animal類是Dog類的基類,Dog類從Animal類派生;也就是會說:基類就是父類,派生類就是子類
1.3.3.1.4繼承的傳遞性
- A類是B類的子類,C類是A類的子類,則C類可以調用B類的方法和屬性
1.3.3.2方法的重寫
1.3.3.2.1覆蓋父類方法,重寫子類方法實現
-
當父類的方法不能滿足子類需求時就需要對方法進行重寫(在子類中定義一個和父類同名的方法,這種重寫只有在調用子類纔會被使用(調用子類的方法時會調用子類中重寫後的方法而不會調用父類的方法),但是父類的同一個方法不會改變,也就是說父類的其它子類調用該方法時仍然是調用父類原先的方法)
class Father: def run(self): print("father run") class Boy(Father): def run(self): print("boy run") class Girl(Father): pass boy = Boy() boy.run() girl = Girl() girl.run()
1.3.3.2.2拓展父類方法,super對象調用父類方法
-
如果在開發中,子類的方法實現中包含父類的方法實現(也就是子類的方法所實現的功能比父類的方法多,父類的方法時子類方法的一部分),就可以通過拓展的方法拓展子類的方法
-
在需要的位置使用super().父類方法來調用父類方法的執行,其它位置就編寫子類的其它功能代碼
-
在python中super是一個特殊的類,super()就是使用super類創建出來的對象,最常使用的場景就是在重寫父類時調用父類中封裝的方法實現
-
簡單來說,super()方法就是在子類重寫父類方法時能夠調用父類原本的方法(而不需要在重寫方法時將父類原本的功能在寫一遍才能調用父類原有的功能)
-
代碼理解
class Father: def run(self): print("father run") class Boy(Father): def run(self): # 針對子類特殊的需求編寫代碼 print("boy run") # 使用super()在重寫父類方法的同時調用父類中原本的方法 super().run() # 增加其它的方法 print("boy walk") class Girl(Father): pass boy = Boy() boy.run() girl = Girl() girl.run()
1.3.3.2.3使用父類名調用父類方法
-
在python2.x中是沒有super()方法的,所有還有一種調用父類方法的方法(在python3.x不推薦使用該方法,python3中推薦使用super()方法來調用父類原有的方法功能)
-
語法:父類名.方法(self)
-
代碼理解
class Father: def run(self): print("father run") class Boy(Father): def run(self): # 針對子類特殊的需求編寫代碼 print("boy run") # 使用父類名.方法(self),這裏的self不能省略 Father.run(self) # 增加其它的方法 print("boy walk") class Girl(Father): pass boy = Boy() boy.run() girl = Girl() girl.run()
1.3.4私有方法和屬性
1.3.4.1子類對象不能直接訪問
- 子類的對象(子類中也是不可以直接訪問父類的私有方法和私有屬性)是不能直接訪問父類的私有方法和私有屬性
1.3.4.2通過父類的方法間接訪問
- 在父類中定義共有方法,在該方法中訪問父類的私有方法和私有屬性,則子類可以通過父類的公有方法來調用父類的私有方法和私有屬性
1.3.5多繼承
1.3.5.1概念、語法
-
子類可以擁有多個父類並且擁有父類的所有屬性和方法,這就稱爲多繼承(一個子類只有一個父類則稱單繼承)
-
多繼承語法
class 子類(父類1, 父類2, ……): pass
-
注意事項:如果一個子類要繼承多個父類注意要保證父類的方法名不同否則會出現歧義(子類到底是調用哪個父類的方法),也就是說如果不同的父類擁有同名的方法應該避免使用多繼承
1.3.5.2MRO方法搜索順序
-
method resolution order的簡稱是MRO(方法解決順序)
-
使用類的內置屬性__mro__可以查看方法的搜索順序(如果父類出現同名的現象可以通過該內置屬性來查看子類調用的是哪一個父類的方法)
class A: def a(self): print("a") class B: def a(self): print("b") class C(A, B): pass c = C() c.a() print(C.__mro__) # 輸出的順序就是方法調用的順序
1.3.5.3新式類和經典類(舊式類)
-
object是python爲所有對象提供的基類,提供一些內置方法和屬性,可以使用dir()函數查看,在python3中所有類的最終基類都是object
-
以object爲基類的類就是新式類,推薦使用;不以object爲基類的類是經典類,不推薦使用;在python3中如果定義一個類時不指定父類就會默認使用object作爲該類的基類,也就是說在python3中所有類的最終基類都是object;在python2中定義的類不會以object爲基類
-
爲了保證編寫的代碼能夠同時在python2和python3上運行,今後在定義類時,如果沒有父類都統一使用object來作爲該類的父類
class 類名(object): pass
1.3.6多態
1.3.6.1基本概念
- 不同的子類對象條用相同的父類方法產生不同的執行結果
- 多態可以增加代碼的靈活度,多態以繼承和重寫父類方法爲前提,多態是調用方法的技巧不會影響到類的內部設計
1.3.7類屬性、類方法、靜態方法
1.3.7.1類屬性
1.3.7.1.1實例
- 概念:爲類創建的對象就叫做實例,通俗來說對象就是實例
1.3.7.1.2類是一個特殊的對象
-
在python中一切皆對象,class 類名:定義的類屬於類對象,對象名 = 類名():定義的對象是一個實例對象
-
在程序運行時類同樣會被加載到內存中
-
類對象可以擁有自己的屬性和方法,這種屬性和方法叫類屬性和類方法;可以通過類名.的方式可以訪問類的屬性或者調用類的方法
-
類名.屬性可以訪問到類屬性;類名.方法名()可以訪問到類方法;類名()可以訪問自定義的實例屬性(__init__定義的實例屬性);對象名.方法名可以訪問實例方法
-
總結:類方法、類屬性、實例方法、實例屬性的區別
class Test: count = 0 # count是一個類屬性而不是實例屬性 def __init__(self): # __init__(self)方法就是一個實例方法而不是類方法 self.sum = 1 # 這裏的sum就是一個實例屬性而不是一個類屬性 @classmethod # 使用該標誌的方法就是類方法 def test(cls): pass
1.3.7.1.3類屬性的定義和應用
-
類屬性就是在類中定義的屬性,通常用來記錄與這個類相關的特徵,類屬性不會用於記錄具體對象的特徵
class Tool(object): count = 0 # 定義一個類屬性count用來統計調用類的對象個數 def __init__(self): Tool.count += 1 # 使用類名.屬性可以方法類屬性 tool_01 = Tool() tool_02 = Tool() tool_02 = Tool() print(Tool.count)
1.3.7.1.4屬性查找機制-向上查找
- 除了通過類名來訪問類屬性還可以通過對象名來訪問類屬性,訪問的語法同類一樣
- 在python中如果用對象名的方式訪問類屬性,python會先在對象中查找是否有該屬性如果有就調用對象中的屬性如果沒有就向上查找對象所屬的類中是否有該屬性,如果沒有就報錯
- 在實際開發中一般推薦使用類名來訪問類屬性
- 使用對象名.變量名=值的方式會在對象的內存中開闢一個變量的空間,並給這個變量賦值
1.3.7.2類方法
1.3.7.2.1基本語法
-
語法格式:
@ classmethod def 類方法名(cls): # 哪一個類調用該方法(類名.方法),cls就是哪一個類的引用 pass # 可以使用cls.方法名來訪問當前類的其它類方法,使用cls.屬性名來訪問當前類的其它類屬性
1.3.7.3靜態方法
-
在開發中如果一個方法既不需要訪問實例屬性或者實例方法也不需要訪問類屬性和類方法那麼就可以將該方法封裝成一個靜態方法
-
格式:
@staticmethod def 靜態方法名(): # 靜態方法不需要指定第一個參數 pass
-
靜態方法的調用:類名.靜態方法名()
1.3.7.4方法綜合
-
案例:
class Game(object): # 定義一個類屬性top_score用來記錄歷史最高分 top_score = 0 # 定義一個實例屬性player_name用來記錄當前遊戲玩家的姓名 def __init__(self, player_name): self.player_name = player_name # 定義一個靜態方法show_help用來顯示遊戲的幫助信息(一般在實際的開發中將不需要訪問類屬性和實例屬性的方法定義爲靜態方法) @staticmethod def show_help(): print("幫助信息") # 定義一個類方法show_top_score用來顯示歷史最高分 @classmethod def show_top_score(cls): print("歷史記錄是%d" % cls.top_score) # 定義一個實例方法start_game用來開始當前玩家的遊戲 def start_game(self): print("%s開啓遊戲" % self.player_name)
-
方法定義的技巧
- 一般而言實例屬性是和對象相關的,不同的對象實例屬性一般不同(即不同對象的同一屬性的值不同那麼該屬性就定義爲實例屬性);而類屬性一般和對象無關(即無論對象爲隨該屬性的值都不變那麼這類屬性就可以定義爲類屬性)
- 如果一個方法內部既需要訪問實例屬性有要訪問類屬性則定義爲實例方法
- 如果一個方法內部需要訪問到實例屬性就定義爲實例方法,實例方法內部可以通過類名來訪問類屬性
- 如果一個方法只需要訪問類屬性就定義爲類方法
- 如果一個方法不需要訪問類屬性和實際屬性就定義爲靜態方法
1.3.8單例模式
1.3.8.1設計模式和單例設計模式的概念
- 設計模式是前人工作的總結和提煉,通常被人們廣泛流傳的設計模式都是針對某一個問題的成熟解決方案;使用設計模式可以更好的保證代碼的可讀性
- 單例設計模式的目的是讓類創建的對象在系統中只有一個唯一的實例;每一次執行類名()返回的對象的內存地址都是相同的(例如一個音樂播放器一次只能播放一首音樂,每臺電腦只有一個回收站,一個打印機一次只能打印一份文件)
1.3.8.2__new__方法
- _new__方法是有object基類提供的一個內置的靜態方法,主要作用有兩個:在內存中爲對象分配空間;返回對象的引用,在實例化對象時python會自動調new方法(也就是說new方法負責在對象創建時爲其開闢一個內存然後返回該內存的地址)_
1.3.8.3重寫__new__方法
class MusicPlayer(object):
def __new__(cls, *args, **kwargs): # 重寫new方法,在創建對象時該方法會被自動調用
return super().__new__(cls)
def __init__(self):
print("播放器初始化")
player = MusicPlayer()
print(player)
1.3.8.4單例設計模式實現
class MusicPlayer(object):
# 記錄第一個類對象的引用
isstance = None
def __new__(cls, *args, **kwargs):
# 判斷類屬性是否時空對象
if cls.isstance is None: # 一般當保存的內容爲對象時可以使用None
# 調用父類的方法,爲第一個對象分配空間
cls.isstance = super().__new__(cls)
# 返回類屬性保存的對象引用
return cls.isstance
player1 = MusicPlayer()
player2 = MusicPlayer()
print(player1)
print(player2)
# 要向讓一個類的不同對象的引用相同就需要重寫類的new方法,來保證返回值都是一樣的
1.3.8.5單例模式下讓初始化方法只調用一次
class MusicPlayer(object):
# 記錄第一個類對象的引用
isstance = None
# 記錄是否執行過初始化
init_flay = False
def __new__(cls, *args, **kwargs):
# 判斷類屬性是否時空對象
if cls.isstance is None: # 一般當保存的內容爲對象時可以使用None
# 調用父類的方法,爲第一個對象分配空間
cls.isstance = super().__new__(cls)
# 返回類屬性保存的對象引用
return cls.isstance
def __init__(self):
# 判斷是否執行過初始化
if MusicPlayer.init_flay == False:
print("初始化")
MusicPlayer.init_flay = True
else:
return
# 這裏的調用一次是指可以讓初始化內部的特定語句只調用一次並不是說初始化方法只調用一次(只要對象被創建初始化方法就會被調用)
player1 = MusicPlayer()
player2 = MusicPlayer()
print(player1)
print(player2)
# 要向讓一個類的不同對象的引用時相同的就需要重寫類的new方法,來保證返回值都是一樣的
1.3.9異常
1.3.9.1捕獲異常
-
在日常的程序開發中如果對某些代碼的執行不能確定是否正確就需要捕獲異常(當特定代碼執行錯誤時程序不會中斷),捕獲異常最簡單的語法格式:
try: 嘗試執行的代碼 # 不能確定正確執行的代碼 except: 出現錯誤的處理 # 出現錯誤時執行的代碼
1.3.9.2錯誤類型捕獲
-
當實際開發中錯誤的類型不止一個時就需要對錯誤的類型進行捕獲,針對不同的類型錯誤得到不同的錯誤提示
-
代碼演示:
try: num = int(input("請輸入一個整數:")) result = 8 / num print(result) except ValueError: # ValueError表示特定的錯誤,獲取的方式就是不寫捕獲異常,報錯後的第一個報錯類型就是錯誤類型 print("值錯誤") except ZeroDivisionError: print("除零錯誤")
-
捕獲未知錯誤,在實際開發中如果有些錯誤是事先無法捕獲到的(就是有些錯誤類型無法提前預知到的),就需要捕獲未知錯誤
except Exception as result: print("未知錯誤%s" % result)
try: num = int(input("請輸入一個整數:")) result = 8 / num print(result) except ValueError: # ValueError表示特定的錯誤,獲取的方式就是不寫捕獲異常,報錯後的第一個報錯類型就是錯誤類型 print("值錯誤") except Exception as result: # 未知錯誤捕獲 print("未知錯誤 %s" % result) # 系統會輸出未知錯誤的錯誤提示
1.3.9.3異常捕獲的完整代碼
-
完整代碼實例
try: # 嘗試執行的代碼 except 錯誤類型1: # pass except 錯誤類型2: # pass except Exception as result: # 捕獲未知錯誤類型 print(result) # 系統會輸出未知錯誤的錯誤提示 else: # 沒有異常纔會執行的代碼 finally: # 無論是否有異常都會執行的代碼
1.3.9.4異常的傳遞
-
當函數/行爲執行出現異常時,會將異常傳遞給函數/方法的調用一方,如果傳遞到主程序時仍然沒有異常處理程序纔會終止
-
在實際開發中,可以在主函數中添加異常捕獲;而在主函數中調用其它函數只要出現異常都會傳遞到主函數的異常捕獲中;這樣就可以保證在編寫代碼時不需要增加大量的異常捕獲保證代碼的整潔
-
代碼實例
def demo1(): b = int(input("請輸入除數:")) sum = 8 / b return sum def demo2(): return demo1() # 利用異常的傳遞性,在主程序中捕獲異常 try: temp = demo2() print(temp) except Exception as result: print(result)
1.3.9.5主動拋出異常
-
拋出raise異常:創建異常對象;拋出異常;捕獲創建的異常對象
def input_password(): # 提示用戶輸入密碼 pwd = input("請輸入密碼:") if len(pwd) >= 8: return pwd else: # 創建異常對象,可以使用錯誤信息字符串作爲參數 ex = Exception("你所輸入的密碼長度不夠") # 主動拋出異常 raise ex try: print(input_password()) except Exception as result: # 這裏會拋出上面所創建的異常對象中的錯誤信息 print(result)
1.3.10模塊和包
1.3.10.1模塊
-
模塊名也是一個標識符,因此在給程序命名時要符合標識符的命名規則
-
給模塊起別名:import 原模塊名 as 原模塊別名(模塊別名要符合大駝峯命名法)
-
在使用from import導入模塊中的方法時,如果兩個模塊擁有同名的方法則後導入的方法會覆蓋原先導入的方法(即實際調用的是後導入模塊中的方法)
-
給模塊中的方法起別名:from 模塊名 import 方法名 as 方法別名
-
注意:在實際開發中from 模塊名 import *這種導入所有方法的形式不推薦使用,因爲當模塊出現同名就很難處理
-
模塊的搜索順序:python的解釋器在導入模塊時會先搜索當前目錄下的指定文件,如果有就直接導入如果沒有就在搜索系統目錄;在python中每一個模塊都有一個內置屬性__file__可以查看模塊的完整路徑
import random print(random.__file__) ran = random.randint(0,10) print(ran)
-
導入一個模塊時,該模塊中所有沒有被縮進的代碼都會被自動執行(不需要被調用),也就是說被導入模塊中所有沒有縮進的代碼都會在調用他時直接被執行
-
__name__屬性可以做到,測試模塊的代碼只有在測試情況下被運行,而在被導入時不會被執行;name屬性是一個內置屬性,記錄着一個字符串;如果是被其它文件導入的,name屬性就是一個模塊名,如果是當前執行的程序,name就是一個main
import test_09
import random print(random.__file__) ran = random.randint(0,10) print(ran) if __name__ == "__main__": # 使用這種形式判讀表明本部分的代碼就是測試代碼,在被導入時不會直接被執行 print(__name__) # 如果執行當前程序,__name__就是一個固定的__main__
import test_09 test_09.main() # 可以通過調用的方式調用模塊中的測試代碼
def main(): print("這是測試代碼") def test(): print("這是被導入代碼") test() if __name__ == "__main__": main()
1.3.10.2包
-
包是一個包含多個模塊的特殊目錄,包名的命名方式和變量名一致,一般使用小寫字母+的方式命名,在一個包下必須有一個特殊的文件__init_.py
-
實例代碼
-
_init_.py
from . import send # 該部分表明要把該包下哪些模塊提供給其它程序使用,不在該部分的模塊其它程序無法調用內部的方法
-
send.py
def test(): print("測試代碼")
-
test.py
import test_001 # 導入包名 test_001.send.test() # 調用包中send方法的test()方法
-
1.3.10.3製作模塊
1.3.10.3.1將包製作成壓縮包併發布壓縮包的步驟
-
創建setup.py文件
from distutils.core import setup setup(name="包名", version="版本", description="描述信息", long_description="完整描述信息", author="作者", author_email="作者郵箱", url="主頁", py_modules = ["要導出的模塊1,格式爲包名.模塊名", "要導出的模塊2"])
-
構建模塊
$ python3 setup.py build
-
生成發佈壓縮包
$ python3 steup.py sdist
1.3.10.3.2安裝模塊壓縮包
$ tar -zxvf 壓縮包名
$ sudo python3 setup.py install
1.3.10.3.3刪除模塊
- 使用模塊的內置屬性__file__查看模塊的具體安裝位置
- 在命令行下切換到模塊的安裝位置
- 使用命令刪除模塊即可
1.3.10.4使用pip安裝第三方模塊
-
第三方模塊的安裝和卸載
$ sudo pip3 install 模塊名 # 將模塊安裝到python3中 $ sudo pip3 uninstall 模塊名 # 將模塊卸載
-
在Mac下安裝ipython
sudo easy_install pip sudo pip install ipython
-
在Linux下安裝ipython
sudo apt install ipython3
1.3.11文件操作
1.3.11.1文件的概念
- 在計算機中,文件是以二進制的方式保存在磁盤上的
- 文本文件
- 可以使用文本編輯軟件查看
- 本質上還是二進制文件
- 例如:python源程序
- 二進制文件
- 保存的內容不是給人直接閱讀的,而是提供給其它軟件使用的
- 例如:圖片文件、音頻文件、視頻文件等
- 二進制文件不能直接用文本編輯器查看
1.3.11.2文件操作
-
在計算機中文件的操作的步驟非常固定:打開文件、操作文件(讀寫)、關閉文件
-
操作文件的函數/方法
函數或方法 說明 open 打開文件並且返回問價操作對象 read 將文件內容追取到內存 write 將指定文件寫入內存 close 關閉文件 -
read/write/close都需要通過文件對象來調用
-
讀取文件內容
-
open函數第一個參數是要打開的文件名,其中如果文件存在,返回文件操作對象,如果文件不存在會拋出異常
-
read函數可以一次性的讀入和返回文件的所有內容
-
如果忘記關閉文件會導致資源消耗,而且會影響後續對文件的訪問
-
代碼實例
# 打開文件 file = open("test") # 讀取文件內容 text = file.read() print(text) # 關閉文件 file.close()
-
-
讀取文件後文件的指針會發生變化
- 文件指針標記從哪個位置開始讀取數據,第一次打開文件時,通常文件指針會指向文件開始的位置,當執行完read方法後文件指針會移動到讀取內容的末尾(也就是說,當執行一次read方法讀取完一個文件後在執行一次read方法就無法讀取文件中的內容)
-
打開文件和寫入數據
-
打開文件的方式:f = open(“文件名”, “訪問形式”)
-
文件的訪問形式
訪問方式 說明 r 以只讀方式打開文件,文件的指針會放在文件的開頭,這是默認的形式,如果文件不存在,拋出異常 w 以只寫方式打開文件,如果文件存在則會被覆蓋,如果文件不存在則創建新文件 a 以追加方式打開文件,如果文件已經存在則文件指針會放在文件的末尾,如果文件不存在創建新文件重新寫入 r+ 以讀寫方式打開文件,文件的指針會放在文件的開頭,如果文件不存在則會拋出異常 w+ 以讀寫方式打開文件,如果文件存在則會覆蓋原文件,如果文件不存在則會創建新文件 a+ 以讀寫方式打開文件,如果文件存在則指針放在文件的結尾,如果文件不存在則會創建新文件 -
在實際開發中爲避免頻繁的使用指針導致文件的寫入混亂通常只使用只讀和只寫
-
代碼實例
# 打開文件 file = open("test", "a+") # 讀取文件內容 text = file.write("def") # 關閉文件 file.close()
-
-
使用readline分行讀取大文件:realline可以一次讀取一行內容,方法執行後文件指針會移動到下一行
-
代碼實例
# 打開文件 file = open("test") # 讀取文件內容 while True: text = file.readline() # 判斷是否讀取,同時實現讀完結束讀取的功能 if not text: break print(text) # 關閉文件 file.close()
-
-
小文件複製
file_read = open("test") file_write = open("test_temp", "w") text = file_read.read() file_write.write(text) file_read.close() file_write.close()
-
大文件複製
file_read = open("test") file_write = open("test_temp", "w") while True: text = file_read.readline() file_write.write(text) if not text: # text爲空時視爲False break file_read.close() file_write.close()
1.3.11.3目錄管理操作
-
在python中如果要實現對文件的刪除、移動、重命名等操作時就需要導入模塊os
-
文件操作相關命令
方法名 說明 實例 rename 重命名文件 os.rename(源文件名, 目標文件名) remove 刪除文件 os.remove(文件名) -
目錄操作相關命令
方法名 說明 實例 listdir 目錄列表 os.listdir(目錄名) mkdir 創建目錄 os.mkdir(目錄名) rmdir 刪除目錄 os.rmdir(目錄名) getcwd 獲取當前目錄 os.getcwd() chdir 修改工作目錄 os.chdir(目標目錄) path.isdir 判斷是否是文件 os.path.isdir(文件路徑) -
以上的目錄名與文件名都需要加括號
1.3.12文本編碼
1.3.12.1文本文件的編碼方式ASCII和UTF8
- python2默認使用ASCII編碼方式(所以不支持中文),python3默認使用UTF8編碼方式
- ASCII編碼:計算機中一共有256個ASCII字符,一個ASCII佔用一個字節的空間(漢字數以萬計無法使用)
- UTF8編碼:計算機中使用1-6個字節來表示一個UTF8字符,涵蓋了地球上幾乎所有的 文字,大多數漢字會使用3個字節表示;UTF-8是UNICODE編碼的一種編碼格式
1.3.12.2如何在python2中使用中文
-
在python2的源文件的第一行增加一行代碼
# *_* coding:utf8 *_*
1.3.12.3python2處理中文字符串
-
遍歷輸出中文字符串時,python2中仍然是以ASCII編碼的方式輸出的(即使寫了# _ coding:utf8 *_*也一樣),而中文在UTF-8中一般使用3個字節,所以輸出中文時會出現亂碼
-
可以在有中文的字符串前面加上一個u,告訴解釋器這是一個utf-8編碼格式的字符串
hel_str = u"你好世界" for c in hel_str: print(c)
1.3.13內建函數eval
1.3.13.1基本使用
-
在實際開發中不要使用eval()函數來轉換input()的輸出結果(因爲在輸入時如果輸入一些特殊的語句會導致文件發生改變)
-
eval函數可以將字符串當成有效的表達式來求值並返回計算結果
a = eval("1+1") # 相當於eval函數可以將引號去掉然後計算引號內部的結果並返回結果 print(a) b = type(eval("[1,2,3]")) print(b) input_str = input("請輸入算術:") print(eval(input_str))