一.初始面向對象python
面向對象的程序的核心是過程(流水線式思惟),過程即解決問題的步驟,面向過程的設計就比如精心設計好一條流水線,考慮周全何時處理什麼東西.linux
優勢是:極大的下降了寫程序的複雜度,只須要順着要執行的步驟,堆疊代碼便可.git
缺點是:一套流水線或者流程就是用來解決一個問題,代碼牽一髮而動全身.程序員
應用場景:一旦完成基本不多改變的場景,著名的例子有Linux內核, git,以及Apach,HTTP,Server等.github
面相對象的程序設計的核心是對象(上帝式思惟),要理解對象爲什麼物,必須把本身當成上帝,上帝眼裏世間存在的萬物皆爲對象,不存在的也能夠創造出來.面相對象的程序設計比如如來來設計西遊記,如來要解決的問題是把經書傳給東土大唐,如來想了想解決這個問題須要四我的:唐僧,沙和尚,豬八戒,孫悟空,每一個人都有各自的特徵和技能(這就是對象的概念,特徵和技能分別對應對象的屬性和方法),然而這並很差玩,因而如來又安排了一羣妖魔鬼怪,爲了防止師徒四人在取經路上被搞死,又安排了一羣神仙保健護航,這些都是對象,而後取經開始,師徒四人與妖魔鬼怪神仙互相纏鬥直到最後取得真經,如來根本不會管師徒四人按照什麼流程去取.編程
面相對象的程序設計的設計模式
優勢是:解決了程序的擴展性,歲某一個對象單獨修改,會馬上反映到整個體系中,如對遊戲中一我的物參數的特徵和技能修改都很容易.微信
缺點:可控性差,沒法向面向過程的程序設計流水線式的能夠很精準的預測問題的處理流程與結果,面向對象的程序一旦開始就由對象之間的交互解決問題,即使是上帝也沒法預測最終結果,因而咱們常常看到一個遊戲人某一參數的修改極有可能致使陰霸的技能出現,一刀砍死3我的,這個遊戲就失去平衡.網絡
應用場景:需求常常變化的軟件,通常需求的變化都集中在用戶層,互聯網應用,企業內部軟件,遊戲等都是面向對象的程序設計大顯身手的好地方.函數
在python中面向對象的程序設計並非所有.
面向對象編程可使用程序的維護和擴展變得更簡單,而且能夠大大提升程序開發效率,另外,基於面向對象的程序可使它人更加容易理解你的代碼邏輯,從而使團隊開發變得更從容.
瞭解一些名詞:類,對象,實例,實例化
類:具備相同特徵的一類事物(人,狗,老虎)
對象/實例:具體的某一個事物(隔壁啊花,樓下旺財)
實例化:類--->對象的過程(這在生活中變現的不明顯,咱們在後面再慢慢解釋)
1.1類的相關知識
聲明
''' def functionName(args): "函數文檔字符串" 函數體 ''' ''' class 類名: "類的文檔字符串" 類體 ''' # 咱們建立一個類 class Data: # 類名首字母大寫 pass
屬性
class Person: # 定義一我的類 role = "person" # 人的角色屬性都是人 def walk(self): # 人均可以走路,也就是說有一個走路方法 print("person is walking.....") print(Person.role) # 查看人的role屬性 print(Person.walk) # 引用人的走路方法,注意,這裏不是在調用
實例化:類名加括號就是實例化,會自動觸發__init__函數的運行,能夠用它來爲每個實例定製本身的特徵
class Person: # 定義一我的類 role = "person" # 人的角色屬性是人 def __init__(self, name): self.name = name # 每個角色都有本身的暱稱 def walk(self): # 人均可以走路,也就是有一個走路方法 print("person is walking") print(Person.role) # 查看人的role 屬性 print(Person.walk) # 引用人的走路方法,注意,這裏不是在調用
實例化的過程就是類------->對象的過程
本來咱們只有一個Person類,在這個過程當中,產生了一個egg對象,有本身具體的名字,攻擊力,和生命值.
語法: 對象名 = 類名(參數)
self
self :在實例化時自動將對象/實例自己傳給__init__的第一個參數,你也能夠給他起個別的名字,可是正常人都不會這麼作.
由於你瞎改別人就不認識
類屬性的補充
一 :咱們定義的類的屬性到底存到哪裏了?有兩種方式查看
dir(類名):查出的是一個名字列表
類名.__dict__:查出的是一個字典,key爲屬性名,value爲屬性值
二 :特殊的類屬性
類名.__name__ #類的名字(字符串)
類名.__doc__ #類的文檔字符串
類名.__base__ #類的第一個父類(在講繼承時會講)
類名.__bases__ #類全部父類構成的元祖(在講繼承時會講)
類名.__dict__ #類的字典屬性
類名.__module__ #類定義所在的模塊
類名.__class__ #實例對應的類(僅新式類中)
1.2對象的相關知識
class 類名: def __init__(self, 參數1, 參數2): self.對象的屬性1 = 參數1 self.對象的屬性2 = 參數2 def 方法名1(self): pass def 方法名2(self): pass 對象名 = 類名(1,2) # 對象就是實例,表明一個具體的東西 # 類名() : 類名+括號就是實例化一個類,至關於調用了__init__方法 # 括號裏傳參數,參數不須要傳self,其餘與init中的形參一一對象 # 結果返回一個對象 對象名.對象的屬性1 #查看對象的屬性,直接用 對象名.屬性名 便可 對象名.方法名1() # 調用類中的方法,直接用 對象名.屬性名 便可
''' 練習一:在終端輸出以下信息 小明,10歲,男,上山去砍柴 小明,10歲,男,開車去東北 小明,10歲,男,最愛大保健 老李,90歲,男,上山去砍柴 老李,90歲,男,開車去東北 老李,90歲,男,最愛大保健 老張… 練習題 ''' class A: def __init__(self,name,age,sex): self.name = name self.age = age self.sex =sex def chop_wood(self): # chop_wood 砍柴 print("%s,%s,%s,上山去砍柴" %(self.name,self.age,self.sex)) def drive(self): # drive 駕駛 print("%s,%s,%s,開車去東北" % (self.name, self.age, self.sex)) def great_health_care(self): # great_health_care 衛生保健系統 print("%s,%s,%s,最愛大保健" % (self.name, self.age, self.sex)) p1 =A("小名",10,"男") p1.chop_wood() p1.drive() p1.great_health_care() p2 = A("老李",90,"男") p2.chop_wood() p2.drive() p2.great_health_care()
1.3類名稱空間與對象的名稱空間
建立一個類就會建立一個類的名稱空間,用來存儲類中定義的全部名字,這些名字稱爲類的屬性
而類有兩種屬性:靜態屬性和動態屬性
靜態屬性就是直接在類中定義的變量
動態屬性就是定義在類中的方法
其中類的數據屬性是共享給全部對象的
>>>id(egg.role) 4341594072 >>>id(Person.role) 4341594072
而類的動態屬性是綁定到全部對象的
>>>egg.attack <bound method Person.attack of <__main__.Person object at 0x101285860>> >>>Person.attack <function Person.attack at 0x10127abf8>
建立一個對象/實例就會建立一個對象/實例的名稱空間,存放對象/實例的名字,稱爲對象/實例的屬性
在obj.nama 會先從obj本身的名稱空間找name,找不到則去類中找,類也找不到就去父類.....最後找不到就拋出異常
二.面向對象的三大特性:繼承,多態,封裝.
2.1繼承
繼承時一種建立新類的方式,在python中,新建的類能夠繼承一個或者多個父類,父類又可稱爲基類或超類,新建的類稱爲派生類或子類
python 中類的繼承分爲:單繼承和多繼承
class ParentClass1: # 定義父類 pass class ParentClass2: # 定義父類 pass class SubClass1(ParentClass1): # 單繼承,基類是ParentClass1 ,派生類是SubClass pass class SubClass2(ParentClass1, ParentClass2): # python 支持多繼承,
用逗號分隔開多個繼承的類 pass
查看繼承
SubClass1.__bases__
#__base__只查看從左到右繼承的第一個子類
#__bases__則是查看全部繼承的父類 (<class '__main__.ParentClass1'>,) SubClass2.__bases__ (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
提示:若是沒有指定基類,python的類會默認繼承object類,object是全部python類的基類,它提供了一些常見的方法(如__str__)的實現.
>>> ParentClass1.__bases__ (<class 'object'>,) >>> ParentClass2.__bases__ (<class 'object'>,)
繼承的重要性
==========================第一部分 例如 貓能夠:爬樹、吃、喝、拉、撒 狗能夠:看門、吃、喝、拉、撒 若是咱們要分別爲貓和狗建立一個類,那麼就須要爲 貓 和 狗 實現他們全部的功能,僞代碼以下: #貓和狗有大量相同的內容 class 貓: def 爬樹(self): print("爬樹") def 吃(self): print("吃") def 喝(self): print("喝") def 拉(self): print("拉") def 撒(self): print("撒") class 狗: def 看門(self): print("看門") def 吃(self): print("吃") def 喝(self): print("喝") def 拉(self): print("拉") def sa(self): print("撒") ==========================第二部分 上述代碼不難看出,吃、喝、拉、撒是貓和狗都具備的功能,
而咱們卻分別的貓和狗的類中編寫了兩次。若是使用 繼承 的思想,以下實現: 動物:吃、喝、拉、撒 貓:爬樹(貓繼承動物的功能) 狗:看門(狗繼承動物的功能) 僞代碼以下: class 動物: def 吃(self): print("吃") def 喝(self): print("喝") def 拉(self): print("拉") def 撒(self): print("撒") # 在類後面括號中寫入另外一個類名,表示當前類繼承另外一個類 class 貓(動物): def 爬樹(self): print("爬樹") # 在類後面括號中寫入另外一個類名,表示當前類繼承另外一個類 class 狗(動物): def 看門(self): print("看門") ==========================第三部分 # 繼承的代碼實現 class Animal: # 定義一個動物類 def eat(self): print("%s吃" % self.name) def drink(self): print("%s喝" % self.name) def shit(self): print("%s拉" % self.name) def pee(self): print("%s撒" % self.name) class Cat(Animal): # 貓 def __init__(self, name): self.name = name self.breed = "貓" def climb_tree(self): # 爬樹 print("%s爬樹" % self.name) class Dog(Animal): def __init__(self, name): self.name = name self.breed = "狗" def door(self): # 門 print("%s看門" % self.name) # ######### 執行 ######### c1 = Cat("小白家的小黑貓") c1.eat() c2 = Cat("小黑的小白貓") c2.drink() d1 = Dog("胖子家的小瘦狗") d1.eat() # 使用繼承來重用代碼比較好的例子
那麼問題又來了,多繼承呢?
是否能夠繼承多個類
若是繼承的多個類每一個類中都定了相同的函數,那麼那一個會被使用呢?
1.python 的類能夠繼承多個類 ,Java 和C#中則只能繼承一個類
2.python 的類若是繼承了多個類,那麼其尋找的方式有兩種,分別是:深度優化和廣度優先
當類是經典類時,多繼承狀況下,會按照深度優先方式查找
當類是新式類時,多繼承狀況下,會按照廣度優先方式查找
經典類和新式類,從字面上能夠看出一個老一個新,新的必然包含了更多的功能,也是以後推薦的寫法,從寫法上區分的話,若是 當前類或者父類繼承了 object 類 ,那麼該類即是新式類,不然即是經典類.
class D: def bar(self): print("D.bar") class C(D): def bar(self): print("C.bar") class B(D): def bar(self): print("B.bar") class A(B, C): def bar(self): print("A.bar") a = A() #執行bar方法 #首先去A類中查找,若是A類中沒有,則繼續去B類中找,若是B類中沒有,則繼續去D類去找,若是D類中沒有,則繼續去C類中找,若是仍是未找到,則報錯 #因此,查詢順序:A-->B-->D-->C #在上述查找bar的過程當中,一旦找到,則尋找過程當即中斷,便不會再繼續找了 a.bar() 經典類多繼承
class D(object): def bar(self): print("D.bar") class C(D): def bar(self): print("C.bar") class B(D): def bar(self): print("B.bar") class A(B, C): def bar(self): print("A.bar") a = A() # 執行bar方法時 # 首先去A類中查找,若是A類中沒有,則繼續去B類中找,若是B類中沒有,則繼續去C類中找,若是C類中麼有,則繼續去D類中找,若是仍是未找到,則報錯 # 因此,查找順序:A-->B-->C-->D a.bar() 新式類多繼承
經典類:首先去A類中查找,若是A類中沒有.則繼續去B類中找,若是B類中麼有,ze繼續去D類中找,若是D類中沒有,則繼續去C類中找,若是仍是未找到,則報錯
新式類:首先去A類中查找,若是A類中沒有,則繼續去B類中找,若是B類中沒有,則繼續去C類中找,若是C類中沒有,則繼續去D類中找,若是仍是未找到,則報錯
抽象類與接口類
一 :繼承基類的方法,而且作出本身的改變或者擴展(代碼重用)
二 :聲明某個子類兼容於某基類,定義一個接口Interface ,接口類中定義了一些接口名(就是函數名)且並未實現接口的功能,子類繼承接口類,而且實現接口中的功能
接口類示例
# 一:這樣很差,咱們要統一一下支付的規則. class QQpay: def pay(self, money): print("使用qq支付%s遠" % money) class Alipay: def pay(self, money): print("使用阿里支付%s元" % money) a = Alipay() a.pay(100) b = QQpay() b.pay(200) # 二:統一支付的規則,歸一化設計,統一 pay接口 class QQpay: def pay(self, money): print("使用qq支付%s元" % money) class Alipay: def pay(self, money): print("使用阿里支付%s元" % money) def pay(obj, moeny): obj.pay("money") a = Alipay() b = QQpay() pay(a, 100) pay(b, 200) # 三,可是,來了一個野生程序員,他不知道你的約定俗稱的規則,就會出問題 class QQpay: def pay(self, money): print("使用qq支付%s元" % money) class Alipay: def pay(self, money): print("使用阿里雲支付%s元" % money) class Wechatpay: def fuqian(self, money): print("使用微信支付%s元" % money) def pay(obj, money): obj.pay(money) a = Alipay() b = QQpay() pay(a, 100) pay(b, 200) c = Wechatpay() c.fuqian(300) # 四 :解決方式 # 定義一個父類,什麼都不寫,只是要求繼承個人全部類的一個pay方法,這樣就制定了一個規範,這就叫作接口,後者抽象類. class Payment: def pay(self): pass class QQpay(Payment): def pay(self, money): print('使用qq支付%s元' % money) class Alipay(Payment): def pay(self, money): print('使用阿里支付%s元' % money) class Wechatpay(Payment): def fuqian(self, money): print('使用微信支付%s元' % money) def pay(obj, money): obj.pay(money) a = Alipay() b = QQpay() pay(a, 100) pay(b, 200) c = Wechatpay() c.fuqian(300) # 五,他仍是不知道看你的這些都繼承了一個類,因此你要定製一個規範,強制他執行 # 建立一個規範 from abc import ABCMeta, abstractmethod class Payment(metaclass=ABCMeta): # 抽象類 接口類 規範和約束 metaclass 指定的是一個元類 @abstractmethod def pay(self): pass # 抽象方法 class Alipay(Payment): def pay(self, money): print('使用支付寶支付了%s元' % money) class QQpay(Payment): def pay(self, money): print('使用qq支付了%s元' % money) class Wechatpay(Payment): # def pay(self,money): # print('使用微信支付了%s元'%money) def recharge(self): pass def pay(a, money): a.pay(money) a = Alipay() a.pay(100) pay(a, 100) # 歸一化設計:無論是哪個類的對象,都調用同一個函數去完成類似的功能 q = QQpay() q.pay(100) pay(q, 100) w = Wechatpay() pay(w, 100) # 到用的時候纔會報錯 #抽象類和接口類作的事情:創建規範 #指定一個類的 metaclass是 ABCMeta #那麼這個類就變成了一個抽象類(接口類) #這個類的主要功能就是創建一個規範
實踐中,繼承的第一種含義意義並不很大,甚至經常是有害的,由於它使得子類與基類出現強耦合.
繼承的第二種含義很是重要,他又叫"接口繼承"
接口繼承實質上是要求"作出一個良好的抽象",這個抽象規定了一個兼容接口,使得外部調用者無需關心具體細節,可一視同仁的處理實現了特定接口的全部對象"-----這個程序設計上,叫作歸一化
歸一化使得高層的外部使用者能夠不加區分的處理全部接口兼容的對象集合---就好象linux 的泛之文件概念同樣,全部的東西均可以當文件處理,沒必要關心它是內存,磁盤,網絡仍是屏幕(固然,對底層設計者,固然也能夠區分"字符設備"和"塊設備",而後作出正針對性的設計:細緻到什麼程度,視需求而定).
依賴倒置原則:
高層模塊不該該依賴底層模塊,兩者都應該依賴其抽象;抽象不該該依賴細節;細節應該依賴抽象,換言之,
要針對接口編程,而不是針對實現編程
在python中根本就沒有一個叫作interface的關鍵字,上面的代碼只是看起來像接口,其實並無起到接口的做用,子類徹底能夠不用去實現接口,若是非要去模仿接口的概念,能夠藉助第三方模塊:
http://pypi.python.org/pypi/zope.interface
twisted的twisted\internet\interface.py裏使用zope.interface
文檔https://zopeinterface.readthedocs.io/en/latest/
設計模式:https://github.com/faif/python-patterns
接口提取了一羣類共同的函數,能夠把接口當作一個函數的集合.
而後讓子類去實現接口中的函數
這麼作的意義在於歸一化,什麼叫歸一化,就是隻要是基於同一個接口實現的類,
那麼全部的這些類產生的對象在使用時,從用法上來講都同樣
歸一化,讓使用者無需關心對象的類是什麼,只須要的知道這些對象都具有某些功能就能夠了,
這極大地下降了使用者的使用難度.
好比:咱們定義一個動物接口,接口裏定義了有跑,吃,呼吸等接口函數,這樣老鼠的類去實現了該接口,
松鼠的類也去實現了該接口,由兩者恩別產生一隻老鼠和一隻松鼠送到你面前,
即使是你分別不到底那只是什麼鼠你確定知道他倆都會跑,都會吃,都能呼吸.
再好比:咱們有一個汽車接口,裏面定義了汽車全部的功能,而後由本田汽車的類,
奧迪汽車的類,大衆汽車的類,咱們都實現了汽車接口,這樣就好辦了,
你們只須要學會了這麼開汽車,那麼不管是本田,仍是奧迪,
仍是大衆咱們都會開了,開的時候根本無需關心我開的是哪一類車,操做手法(函數調用)都同樣
抽象類
什麼是抽象類
與Java同樣,python也有抽象類的概念可是一樣須要藉助模塊實現,抽象類是一個特殊的類,它的特殊之處在於只能被繼承,不能被實例化
爲何要有抽象類