1 面向過程編程
以指令爲核心:圍繞「正在發生什麼」進行編寫
面向過程:程序= 算法+ 數據結構
面向過程編程:程序具備一系列線性步驟,主體思想是代碼做用於數據,以指令爲核心,核心是設計算法,
2 面向函數的編程
3 面向對象的編程python
面向對象編程--object oriented programming ,簡稱OOP,把對象做爲程序的基本單元,一個對象包含了數據和操做數據的函數
面向過程把函數繼續切分稱爲子函數,來下降系統的複雜度。
面向對象編程(OOP)
程序 = 指令 + 數據/算法 + 數據結構
以數據爲核心: 圍繞「將影響誰」進行編寫
面向對象編程(OOP):圍繞數據以及爲數據嚴格定義的接口來組織程序,用數據控制對代碼的訪問,面向數據,以及可以對這些數據採起的操做進行,代碼執行過成,不能跳過數據
面向對象編程的核心概念:
全部編程語言的最終目的都是提供一種抽象方法
在機器模型(「解空間」或「方案空間」)與實際解決問題的問題模型(「問題空間」)之間,程序員必須創建一種聯繫
將問題空間中的元素以及他們在解空間中的標識物抽象爲對象,並容許經過問題來描述問題而不是經過方案來描述問題
能夠把實例(某個類的實例)想象成一種新型變量,他保存這數據,但能夠對自身數據執行操做,實例能夠對自身數據作操做程序員
定義了被多個同一類型對象共享的結構和行爲(數據和代碼)
類是抽象的,實例是具體的,類是(概念,抽象的概念模型),類自己是不能被操做的,須有實例化的對象才能操做
類內部有兩個核心成員:代碼和數據,都叫類成員
數據:成員變量或實例變量
成員方法: 簡稱爲方法,是操做數據的代碼,用於定義如何使用成員變量,所以一個類的行爲和接口是經過方法來定義的
類:將同一種具體的物事的共同特性抽想出來的表現算法
實例是類的具體表現形式,類存在的目的就是爲了實例化。類是抽象的,實例是具體的編程
calss 類名(父類):
類的內容
類後面有括號的稱爲新式類,沒有括號稱爲經典類。
括號裏面的內容是父類的名稱,程序中,全部父類都是object設計模式
類的查看:python3.x
當類的實例化後,類的屬性是不變的安全
類的數據屬性是可變的,當在實例處修改類的數據屬性時,其類自己的數據屬性不變。數據結構
當類的數據屬性發生改變時,本來已經改變的實例的數據屬性不會再次改變,而新建立的實例的數據屬性會發生改變app
#!/usr/local/bin/python3.6 #coding:utf-8 class MyClass: '''is example class''' x='abc' # 類的屬性 def foo(self): # 類的屬性,也是方法 return (self,'MyClass') myclass=MyClass() # 進行實例化操做 print (myclass.x) #調用類的屬性 print (MyClass.x) # 類調用類屬性 print (MyClass.foo(1)) # 類調用類方法 print (myclass.foo()) # 調用類的方法 print (type(myclass)) # 打印類型 print (id(MyClass)) # 打印類的實例地址 print (id(myclass)) # 打印類的內存地址
結果以下 dom
其實例化都的內存地址和類自己的內存地址不一樣,且實例能夠調用類的屬性和方法,類自身也能夠進行相關的調用處理
在類中定義的函數教方法,類的方法中,要求第一個形參必須是self,而self其實是類實例化後的對象自己
實例化後得到的實例,是不一樣的實例,即便使用相同的參數實例化,也獲得不同的對象,其會先調用new 進行實例化,而後調用_init_ 進行初始化
實例化後,會自動調用__init__方法,這個方法的第一個參數必須留給self,其餘參數隨意
類方法的定義
類方法的調用
#!/usr/local/bin/python3.6 #coding:utf-8 class MyClass: '''is example class''' x='abc' # 類的屬性 y='cbd' def __init__(self): # 初始化時進行打印,並顯示結果,其__init__方法中不能return,其只能用作初始化 print ('init') def foo(self): return (self.x) def foo1(self): return (self.y) myclass=MyClass() # 對象實例化 print (myclass.foo()) print (myclass.foo1())
結果以下
#!/usr/local/bin/python3.6 #coding:utf-8 class Person: def __init__(self,name,age=20): self.name=name # 此處的name始終和上述的self後面的name相對應,下面相同 self.x=age def showage(self): print ('{} is {}'.format(self.name,self.x)) # 此處self.x 始終和上面的相同,其中age至關於形參 tom=Person('tom') #對參數進行實例化操做 jerry=Person('jerry',10) # 對參數進行實例化 tom.showage() #打印其方法 jerry.showage() a=Person('a') # 其獲取到的參數徹底相同 b=Person('a') print (a is b) # 其內存地址不一樣 print (a==b) # 其獲取結果不一樣,
結果以下
#!/usr/local/bin/python3.6 #coding:utf-8 class Person: x='abc' def __init__(self,name,age=18): self.name=name self.age=age def show(self,x,y): # 經過此處的參數,可修改類中的屬性 self.name=x # 修改name的屬性 self.age=y Person.x=x # 修改類屬性 print (self.name,self.age,x) tom=Person('tom') tom.show('jerry',20) print (Person.x) # 其返回結果爲修改類屬性後的結果
結果以下
1 class._name_ 類的名稱顯示
2 class._doc_ 類的幫助文檔
3 class._base_ 類的父類/基類
4 class._module_ 類的模塊,當不是導入的模塊的類時,其執行結果爲main,當爲模塊導入時,其執行結果爲模塊名
5 class._dict_ 對象屬性的字典,用於遍歷和查找屬性,每一個對象中保存這本身的屬性,類的屬性在類字典中,實例的屬性在實例的字典中
6 class._class_ 對象的類型,__class__和type中的類型是相同的,在python3中相同,在早期的python2 中有某些事不相同的
7 class._qualname_ 類的限定名
#!/usr/local/bin/python3.6 #coding:utf-8 class Person: x='abc' def __init__(self,name,age=18): self.name=name self.age=age def show(self,x,y): # 經過此處的參數,可修改類中的屬性 self.name=x # 修改name的屬性 self.age=y Person.x=x # 修改類屬性 print (self.name,self.age,x) tom=Person('tom') print (tom.__class__,Person.__class__) # 打印實例類型和類的類型 print (tom.__dict__,Person.__dict__) # 打印實例的屬性字典和類的屬性字典 print (tom.__qualname__,Person.__qualname__) # 打印實例的限定名和類的限定名,默認的,只有類有限定名
結果以下
如需輸出,則須要
#!/usr/local/bin/python3.6 #coding:utf-8 class Person: x='abc' def __init__(self,name,age=18): self.name=name self.age=age def show(self,x,y): # 經過此處的參數,可修改類中的屬性 self.name=x # 修改name的屬性 self.age=y Person.x=x # 修改類屬性 print (self.name,self.age,x) tom=Person('tom') print (tom.__class__.__qualname__) #此處經過__class__吊起該實例的類,經過類調用其限定名便可 print (isinstance(tom,Person)) #經過此處判斷該類是不是該實例的類 print(isinstance(tom,tom.__class__))
結果
#!/usr/local/bin/python3.6 #coding:utf-8 class Person: x='abc' def __init__(self,name,age=18): self.name=name self.age=age def show(self,x,y): # 經過此處的參數,可修改類中的屬性 self.name=x # 修改name的屬性 self.age=y Person.x=x # 修改類屬性 print (self.name,self.age,x) tom=Person('tom') jerry=Person('jerry',20) print (tom.__dict__,jerry.__dict__,Person.__dict__,sep='\n')# 打印實例和類的屬性字典
結果以下
#!/usr/local/bin/python3.6 #coding:utf-8 class Person: x='abc' age=3 height=170 def __init__(self,name,age=18): self.name=name self.age=age tom=Person('tom') jerry=Person('jerry',20) Person.age=30 print (Person.__dict__,tom.__dict__,jerry.__dict__,sep='\n') #此處打印類和實例對象屬性字典 print (Person.age,tom.age,jerry.age) # 結果爲30,18,20 Person.height+=30 print (Person.__dict__,tom.__dict__,jerry.__dict__,sep='\n') print (Person.height,tom.height,jerry.height) # 結果爲200,200,200 tom.height=180 # 當此處給單獨的對象添加了類的屬性並賦值後,其對象tom的屬性字典中的值也會隨之變化 print (Person.__dict__,tom.__dict__,jerry.__dict__,sep='\n') # 此處tom的字典屬性中也增長了height的key,且其值爲180 print (Person.height,tom.height,jerry.height) #結果爲200,180,200 jerry.height+=30 # 此處添加了實例的屬性後,其對象字典中的對象屬性也會隨之增長 print (Person.__dict__,tom.__dict__,jerry.__dict__,sep='\n') # 此處jerry 的字典屬性中也增長了height的key,且其值爲230 print (Person.height,tom.height,jerry.height) # 結果爲200,180,230
結果以下
#!/usr/local/bin/python3.6 #coding:utf-8 class Person: x='abc' age=3 height=170 def __init__(self,name,age=18): self.name=name self.age=age tom=Person('tom') print (tom.__dict__['name'],tom.__dict__['age']) # 經過key調用對應的value,此處不能調用height,由於其不在tom屬性字典中
結果以下
是類的,也是這個類全部實例的,其實例均可以訪問到,是實例的,就是這個實例本身的,經過類訪問不到。
實例能夠動態的給本身增長一個屬性,實例.dict[變量名]和實例.變量名均可以訪問到
實例的同名變量會隱藏這類變量,或者說是覆蓋了這類變量
實例屬性的查找順序
指的是實例使用.來訪問屬性,會先找到本身的dict,若是沒有。則經過屬性class找到本身的類,再去類的dict中找注意: 若是實例使用dict[變量名]訪問變量,將不會按照上面的順序查找變量了
經過類方法@classmethod 進行將類自己調用,其中cls 關鍵字就是表示類自己,
可使用類名.類方法(參數) 的形式進行調用。並使用return 返回結果
1 在定義類中,使用@classmethod 裝飾器修飾的方法
2 必須至少傳遞一個參數,且第一個參數留給了cls,cls指代調用者及類對象自身
3 cls這個標識符能夠是任何合法名稱,可是爲了易讀,請不要修改
4 經過cls能夠直接操做類的屬性,其沒法對實例的屬性進行修改,由於其第一個傳遞的是類對象,類中也不能調用實例的方法
相關實現
#!/usr/local/bin/python3.6 #coding:utf-8 class Document: # 父類 def __init__(self,content): self.content=content def print(self): # 共有的屬性 print (self.content) class Word(Document): pass class PrintableWord(Word): def print(self): # 子類本身的私有屬性 print ("Word pinrt {}".format(self.content)) class Pdf(Document): pass # 子類自定義私有屬性 print (PrintableWord.mro()) word=PrintableWord('test\nabc') word.print()
結果以下
實例以下
#!/usr/bin/poython3.6 #conding:utf-8 class MyClass: x='123' @classmethod # 類方法,此處傳入的第一個值是類,cls.__name__調用的是這個類的名稱,而cls.x調用的是這個類的私有屬性 def my(cls): cls.pas='456' # 可經過此種方式在類中增長屬性 print ('{}.x={}'.format(cls.__name__,cls.x),cls.pas) MyClass.my() # 此處是類的自調用 a=MyClass() # 實例化,其類的相關屬性依然存在 a.my()
結果以下
1 在定義類中,使用@staticmethod 裝飾器裝飾的方法
2 調用時,不會隱式傳入參數
3 靜態方法,只是代表這個方法屬於這個名稱空間,函數歸類在一塊兒,方便管理組織
實例以下
#!/usr/bin/poython3.6 #conding:utf-8 class MyClass: def char(): #此處無語法錯誤,但其不建議如此寫入 print ('char') @staticmethod # 此處是靜態方法,用於保存類中的靜態變量,其自己和實例無任何關係 def char1(): print ('char1') a=MyClass() # a.char() # 此處調用會出錯,但下面的因爲是靜態方法,所以其調用不會出錯 a.char1()
總結: 靜態方法和類方法都屬於類的屬性
此處不推薦直接使用def char()的方式進行處理,而推薦使用@staticmethod,python中對靜態方法的使用較少,
datetime 是在類上調用的方法,進而造出datetime對象而返回這個方法
python中常見的三種函數:
1 和實例相關的函數
2 和類相關的函數
3 通常的函數類的方法在類建立完成後便可進行調用,而實例的方法在類建立完成以後不能調用
類的方法調用
類幾乎能夠調用全部內部定義的方法,可是調用普通的方法時會報錯,緣由是第一個參數必須是類的實例
實例幾乎能夠調用全部的方法,普通的函數的調用通常不可能出現,由於不容許這麼定義
類方法:默認第一個參數是類自己,其能夠被類調用,不能被實例調用
普通方法:默認第一個參數是實例自己,能夠被類和實例進行調用
靜態方法:默認第一個參數數傳入的參數。只能被類自己進行調用,不能被實例調用
總結:
類除了普通方法外均可以調用,普通方法須要對象的實例做爲第一參數
實例能夠調用全部類中的方法(包括類方法,靜態方法),普通方法傳入實例自身,靜態方法和類方法須要找到實例的類後進行相關的處理。
擴展:
在類中調用類自己的方法:
傳統的輸入年、月、日、的方法:
經過函數進行格式化的結果,經過在函數中調用類的方式實現
使用雙下劃線開頭的屬性名,就是私有屬性
經過在類的構造時使用self.__x=x 來 獲取類的私有屬性。
默認的類的私有屬性不能訪問:
經過將其私有屬性包裝成方法進行使用,其是能夠訪問的。
實例以下:
#!/usr/bin/poython3.6 #conding:utf-8 class Person: def __init__(self,name,age): self.name=name self.age=age def char(self): if 0< self.age < 100: return self.age tom=Person('tom',50) print (tom.char()) jerry=Person('jerry',110) print (jerry.char()) # 此處的返回爲空,由於沒有知足的條件 print (jerry.age) # 可經過此種方式進行返回,所以其是沒法真實控制的
結果以下
此處python提供了私有屬性用於解決此種問題
#!/usr/bin/poython3.6 #conding:utf-8 class Person: def __init__(self,name,age): self.name=name self.__age=age # 私有屬性,隱藏以後沒法使用實例名.__age進行調用,必須將其抽象成一個方法並輸出的方式進行處理 def char(self): if 0< self.__age < 100: return self.__age # def charget(self): return self.__age tom=Person('tom',50) print (tom.char()) jerry=Person('jerry',110) # print (jerry.__age) #此處沒法經過__age的方法進行調用嗎 print (jerry.charget()) #此處可有方法進行調用處理 print (jerry.__dict__) # 事實上,此處的私有屬性在python中是修改了名稱,其結果是實例名._類名稱__age可進行調用,具體可參見__dict__獲取,此處私有屬性是在實例字典中存在的 print (jerry._Person__age) jerry._Person__age=300 #修改私有屬性參數值,但其測試,顯示中既然是私有屬性,則不建議修改其屬性值 print (jerry._Person__age) # 修改結果查看
結果以下
私有變量的本質: 類定義的時候,若是聲明一個實例變量的時候,使用雙下劃線,python解釋器會將其更名,轉換成爲實例名._類名__變量名,因此原來的名字訪問不到了,私有屬性是在實例的字典中的
在變量名前使用一個下劃線,稱爲保護變量
#!/usr/bin/poython3.6 #conding:utf-8 class Person: def __init__(self,name): self._name=name # 保護變量,python開發人員約定俗稱的,不可修改的,但實際其是能夠被實例調用且修改的,但建議其使用方法進行封裝處理 def printf(self): return self._name tom=Person('tom') print (tom._name) tom._name='jerry' print (tom.printf()) #修改後打印
結果以下
能夠看出,這個_name屬性根本就沒改變名稱,和普通的屬性同樣,解釋器不作任何特殊處理,這只是開發者共同的約定,看見這種變量,就如同私有變量,不要直接使用,保護變量也是在實例的字典中的。
#!/usr/bin/poython3.6 #conding:utf-8 class Person: def __init__(self,name): self.name=name def __printf(self): #私有方法,其本質是對其名稱作了修改,其方法是類的方法是Person.__printf return self.name tom=Person('tom') print (Person.__dict__) # print (tom.__printf()) #修改後打印 print (tom.__dict__) # 實例列表中無此方法 print (tom._Person__printf()) # 此處的調用先找到實例,而後沒有這方法,而後找到類,找到該方法,
結果以下
在函數上的雙下劃線,是屬於類的屬性,方法內部(函數)的雙下劃線,是屬於實例的屬性,而不是類的屬性
私有方法的本質
單下劃線的方法只是開發者之間的約定,解釋器不會作任何改變
雙下劃線的方法,是私有方法,解釋器會更名,改變策略和私有變量相同,但其私有變量是在實例字典中,而私有方法倒是在類字典中
私有成員總結
在python中使用單下劃線或者雙下劃綫來標識一個成員被保護或者私有化隱藏起來,可是,無論使用什麼樣的方式訪問控制,都不能真正的阻止用戶修改類的成員屬性,python中沒有絕對安全的保護成員或者私有成員所以,前面的下劃線是一種警告或者提醒,請遵照這個約定,除非真的必要,不然不要修改隨意使用成員保護或者私有成員。
屬性裝飾器的目的: 把實例的屬性保護起來,不讓外部直接訪問,外部使用getter讀取屬性和setter方法設置屬性。
通常方法
#!/usr/local/bin/python3.6 #coding:utf-8 class Person: def __init__(self,name,age=18): self.__name=name self.age=age def getname(self): #查看名稱的屬性 return self.__name def setname(self,name): # 修更名稱屬性 self.__name=name tom=Person('tom') print (tom.getname()) tom.setname('jerry') print (tom.getname())
結果以下
屬性裝飾器
#!/usr/local/bin/python3.6 #coding:utf-8 class Person: def __init__(self,name,age=18): self.__name=name self.age=age @property def getname(self): #查看名稱的屬性 return self.__name @getname.setter #修改屬性值,此處的getname和上面的和下面的必須相同,不然會出現錯誤 def getname(self,value): self.__name=value @getname.deleter # 刪除屬性值,使用del self.__name 進行對屬性的刪除操做 def getname(self): #del self.__name print ('delete self.__name') tom=Person('tom') print (tom.getname) tom.getname='jerry' # 修改屬性值 print (tom.getname) del tom.getname #外部調用執行刪除程序
結果以下
第二種寫法
#!/usr/bin/poython3.6 #conding:utf-8 class Person: def __init__(self,chinese,english,history): self._chinese=chinese self.__english=english def seteng(self,value): # 只有getter屬性,此中成爲只讀屬性 self.__english=value #第二種寫方式,初始化方法 eng=property(lambda self:self.__english,seteng ) # property 是隻讀的,此處的傳入的值,eng是一個函數,第一個是gettter,第二個是setter stu=Person(90,100,110) print (stu.eng) stu.eng=200 #修改其值 print (stu.eng)
結果以下
經過此裝飾器@property: 定義一個類方法爲私有屬性的名稱;讓用戶能夠直接訪問, 但不能任意修改;
經過其裝飾器的方法進行從新定義一個接受隱藏屬性的範圍來進行修改其值。
@屬性名.seeter: 給屬性賦值時先作判斷; 當屬性名=value會自動調用該函數
經過deleter 方法可使得當del 屬性名, 會自動調用該函數並執行高函數下面的操做~~
特別注意: 這三個方法同名
property 裝飾器
後面跟的函數名就是之後的屬性名,它就是getter,這個必須有,有了它至少是隻讀屬性setter裝飾器
與屬性名同名,且接受2個參數,第一個是self,第二個是將要賦值的值,有了它,屬性可寫deleter 裝飾器
能夠控制是否刪除屬性,不多用property裝飾器必須在前,setter、deleterious裝飾器在後
property 裝飾器能經過簡單的方式,把對象方法的操做編程對屬性的訪問,並起到了必定的隱藏做用
@property應用:
用於分頁顯示,此處傳入兩個值,一個值是第幾頁,另外一個是每頁的數量,經過@property 能夠進行對列表的操做。
類中能夠定義del方法,稱爲析構函數(方法)
做用: 銷燬類的實例的時候調用,用以釋放佔用的資源 ,其目標是銷燬實例因爲python實現了垃圾回收機制,這個方法不能肯定什麼時候使用,有必要的時候,請使用del語句刪除實例
#!/usr/bin/poython3.6 #conding:utf-8 class Person: def __init__(self,name,age=20): self.name=name self.__age=age def __del__(self): # 設置消亡實例 print ("delete instance {}".format(self.name)) a=Person('tom') del a # 調用消亡實例
結果以下
在其餘面向對象的高級語言中,都有重載的概念,所謂的重載,就是同一個方法名,可是參數數量,類型不同,就是同一個方法的重載
python 沒有重載,python不須要重載
python中,方法(函數)定義中,形參很是靈活,不須要指定類型,參數個數也不固定,一個函數的定義能夠實現多種不一樣的形式,所以python不須要重載。
python中只要標識符相同,則會被徹底覆蓋,
1 隨機數生成類,能夠指定一批生成的個數,能夠指定數值的範圍,生成隨機數
#!/usr/bin/poython3.6 #conding:utf-8 import random class randsum: @classmethod def getsrandsum(cls,min=1,max=10,num=10): return [random.randint(min,max) for _ in range(num)] print (randsum.getsrandsum()) print (randsum.getsrandsum(10,20,5))
結果以下:
2 隨機生成隨機數,並兩兩配對造成二維座標系,把這些座標組織起來,並進行打印
#!/usr/bin/poython3.6 #conding:utf-8 import random class RandomNum: def __init__(self,count=10,min=1,max=10): self.count=count self.min=min self.max=max self.g=self._generate() # 此處可以使用此種方式將內部的方法以屬性的形式進行處理,供內部調用 def _generate(self): while True: yield [random.randint(self.min,self.max) for _ in range(self.count)] #c此處返回一個列表生成式,用於下面的next()進行調用 def genrate(self,count): self.count=count return next(self.g) # 此處經過此種方式進行處理,返回值爲隨機數的單個個體 a=RandomNum() for k,v in dict(zip(a.genrate(5),a.genrate(5))).items(): print ("(x={},y={})".format(k,v))
結果以下
3 記錄車的品牌mark,顏色color,價格 price,速度等特徵,並實現增長車輛信息,顯示所有車輛信息的功能。
#!/usr/bin/poython3.6 #conding:utf-8 class Car: def __init__(self,mark,speed,color,price): self.mark=mark self.speed=speed self.color=color self.price=price class CarInfo: # 信息自己也是一個對象,一個容器的對象 def __init__(self): self.lst = [] def addcar(self,car:Car): #此處可調用上述的類 self.lst.append(car) def getall(self): return self.lst # 打印的格式化輸出 ci=CarInfo()# 此處完成後,其基本的列表建立已經完成 car=Car('audi',100,'red',1500000) # 此處是初始化 ci.addcar(car) # 調用此初始化數據並增長 print (ci.getall()) # 查看
能夠經過修改或者替換成員,使用者調用方沒改變,可是,類提供的功能可能已經修改了,當項目須要修改較多的類時,又着急上線,修改起來比較麻煩時,須要經過在一個文件中指定某個類進行相關的操做來對其進行打補丁
猴子補丁
運行時,對屬性進行動態替換
黑魔法,慎用
test 源文件
#!/usr/bin/poython3.6 #conding:utf-8 class Person: def __init__(self,chinese,english,history): self.chinese=chinese self.english=english self.history=history def getscore(self): return (self.history,self.english,self.chinese) a=Person(70,80,90) print (a.getscore())
默認結果以下
補丁文件
test1
#!/usr/bin/poython3.6 #conding:utf-8 def getscore(self): # 其名稱和相關的類對象名必須一致,不然會報錯,此處返回是一個字典 return dict(chi=self.chinese,eng=self.english,his=self.history)
test2
#!/usr/bin/poython3.6 #conding:utf-8 from test import Person from test1 import getscore def monkeypatch4Persoon(): Person.getscore=getscore # 對類來改變屬性,在類的DICT中有方法, student = Person(80,91,80) print (student.getscore()) monkeypatch4Persoon() student1 = Person(80,91,80) print (student1.getscore())
結果以下
組裝: 將數據和操做組裝到一塊兒
隱藏數據:對外只暴露一些接口,經過接口訪問對象
封裝其實是把數據封裝到摸個地方,之後再去調用在某處的內容或者數據
調用封裝數據的方式
經過對象直接調用
其中init表示的是一個構造器,當類的實例化過程時會調用其方法,因爲其是必選參數,所以在傳值時必須與對應的個數相同,固然能夠實例化多個對象
經過self在類內部對類中的數據進行調用
類的實例化
多複用,繼承來的就不用本身寫了
多繼承少修改,OCP,使用繼承來改變,來提現個性
基類和派生類
其中父類也叫基類
子類也叫派生類
父類
子類,其中沒有定義類的屬性和方法,只是繼承了Class1 的屬性
子類的實例化和結果,其徹底繼承和父類的屬性和方法:
當子類中有本身的構造函數時,以子類中的構造函數爲準
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name): self.__name=name @property def name(self): # 此處的A使用裝飾器將方法封裝成屬性並進行暴露 return self.__name def shout(self): print ('A shout') class B(A): #此處的B 繼承了A的屬性 x=456 def shout(self): # 此處的B對A的方法進行了重寫 print ('B shout') class C(B): pass tom=C('tom') print (tom.name) tom.shout()
結果以下
默認的,私有屬性不能直接調用,須要假裝成方法方可調用。
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name='tom',age=10): self.name=name self.__age=age def shout(self): print ('A shount') @property def getage(self): return self.__age class B(A): x=456 def shout(self): print ('B shout') jerry=A('tom') print (jerry.__dict__) tom=B('jerry') print (tom.__dict__) # 默認的A的私有屬性會被B讀取,並可進行訪問,其方法亦可被訪問 print (tom.getage)
結果以下
在類中,以雙下劃綫開始的方法稱爲私有方法:私有方法不能繼承。
#!/usr/bin/poython3.6 #conding:utf-8 class A: def __init__(self,name,age): self.name=name self.age=age def __getname(self): #此處定義私有方法 return self.name class B(A): pass b=B('tom',30) print (b.__dict__) print (B.__dict__) a=A('jerry',20) print (a.__dict__) print (A.__dict__) # 私有方法是屬於類的,不能被繼承,
結果以下:
特殊屬性和方法 | 含義 |
---|---|
_base_ | 類的基類 |
_bases_ | 類的基類元組 |
_mro_ | 顯示方法查找順序,基類的元組 |
mor() | 同上 |
_subclasses_() | 類的子類列表 |
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name): self.__name=name @property def name(self): # 此處的A使用裝飾器將方法封裝成屬性並進行暴露 return self.__name def shout(self): print ('A shout') class B(A): #此處的B 繼承了A的屬性 x=456 def shout(self): # 此處的B對A的方法進行了重寫 print ('B shout') class C(B): pass print (C.__base__) #顯示此類的基類 print (C.__bases__) # 顯示此類的基類,以元組呈現 print (C.__mro__) # 顯示類的鏈表,由此類的父類開始查找 print (C.mro()) # 通上 print (A.__subclasses__()) # 顯示此類的子類
結果以下
類和實例屬性列表打印
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name): self.__name=name @property def name(self): # 此處的A使用裝飾器將方法封裝成屬性並進行暴露 return self.__name def shout(self): print ('A shout') class B(A): #此處的B 繼承了A的屬性 x=456 def shout(self): # 此處的B對A的方法進行了重寫 print ('B shout') class C(B): pass tom=C('tom') print (tom.__dict__) # 打印實例的字典,其中只有實例化的屬性的列表 print (C.__dict__) # 打印C類的字典,C類無任何屬性和方法,由於其是來自於B和A的 print (B.__dict__) # 打印B類的字典,其中有x類屬性的定義,shout函數的定義 print (A.__dict__)# A中包括初始化函數等信息
以下
類和類屬性只有一個,而實例和實例屬性卻有多個
凡是須要分紅多個的,應該設計成類的實例屬性,凡是須要一份的,應該設計成類的屬性
一個子類繼承了祖先的特徵,但祖先的都是抽象的東西,只有對象才具備繼承數據的能力,對於實例,不一樣的須要設置各個單獨的屬性,須要放置在本身的屬性列表中,若是須要一份,則放置在類屬性上。
A.init(self,args)
1 經過 父類名.init(self,父類參數) 進行重寫構造函數
經過super(本身類名稱,self).init(形參)
優勢: 不須要明確告訴父類的名稱,若是父類改變,只需修改class 語句後面的繼承關係便可,不用修改父類名稱
實例化調用
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def shout(self): print ('A shout') class B(A): def shout(self): #此處定義覆蓋了父類的shout print ('B') def shout(self): # 此處定義覆蓋了自身的shout print (super()) # 顯示super內容 print (super(B,self))# 此處至關於將B設置爲self傳輸進去。等價與上面的super super().shout()#此處調用的是父類並調用了父類的shout a=B() a.shout()
結果以下
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name='tom',age=0): self.__name=name # 此處是實例的初始化函數,固然要在其實例化後才能表現出該屬性,該類中也找不到,固然在其子類中也不能找到 self.age=age @property def getname(self): return self.__name def shout(self): print ('A shout') class B(A): x=456 def __init__(self,name,age): self.age=age # 此處的傳入後會修改爲50,由於其是在修改以後傳入A類中進行處理的 super().__init__(name,age) #新式類推薦使用的方式,此處默認傳入self,須要送入指定參數,此處不準找指定self和父類的類名 self.__name=name+" "+'B' # 私有屬性的修改是沒法生效的,由於其私有屬性的key和類名相關, # self.age=10 #覆蓋共有屬性,其以前的age是0,如今覆蓋成10,由於實例的字典中的key是惟一的。此處由於在a.__init__(self,name)以後定義,所以其會 # 覆蓋以前name傳入的值 def shout(self): print ('B shout') super(B,self).shout() #此處是調用父類的,經過super能夠方面的訪問本身的祖先類,其查找順序是找本實例,若經過本實例查找 #其類,經過類查找其父類並進行相關的操做 super().shout() # 與上述相同 x=B('jerry',30) # 此處傳入的值是30,因爲下面的覆蓋,變成了10 x.shout() print (x.__dict__) #其私有屬性的_A__name 仍然存在,_B__name 也存在, print (x.getname)
結果以下
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name='tom',age=10): self.name=name self.age=age def shout(self): print ('A shount') class B(A): x=456 def __init__(self,name): #此處進行初始化,初始化後,其父類的公有屬性將不能被調用 self.name=name #此處從新覆蓋了共有屬性name,由以前的tom修改爲了jerry def shout(self): print ('B shout') tom=B('jerry') print (tom.name) print (tom.__dict__) #print (tom.age) # 此處是父類的屬性,此處不能被調用
結果以下
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name='tom',age=10): self.name=name self.age=age def shout(self): print ('A shount') class B(A): x=456 def __init__(self,name): #此處進行初始化,初始化後,其父類的公有屬性將不能被調用 self.cname=name #此處定義了公有屬性cname def shout(self): print ('B shout') tom=B('jerry') print (tom.cname) # print (tom.name) #此處是父類的屬性,不能被調用,由於子類進行了初始化 print (tom.__dict__) # 此處只有cname #print (tom.age) # 此處是父類的屬性,此處不能被調用
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name='tom'): self.name=name def shout(self): print ('A shount') class B(A): x=456 def __init__(self,name): #此處進行初始化,此處須要調用父類的方法,若不調用,則默認父類的私有方法不能使用,但共有方法能夠被調用 self.cname=name #此處從新定義了共有屬性cname, A.__init__(self,name) # 此處調用了父類的name def shout(self): print ('B shout') tom=B('jerry') print (tom.name) #此處name存在於A類中。而B中調用A.__init__(self,args) 致使其A中存在的屬性在B中也一樣存在 print (tom.cname) print (tom.__dict__)# 其共有方法中的name存在,也有本身的初始化的cname
結果以下
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name='tom',age=0): self.__name=name self.age=age @property def getname(self): return self.__name def shout(self): print ('A shout') class B(A): x=456 def __init__(self,name,age): #self.age=age # 此處的傳入後會修改爲30,由於其是在修改以後傳入A類中進行處理的 A.__init__(self,name,age) #經過父類的調用來調用父類的屬性,此中可調用父類的私有屬性, self.__name=name+" "+'B' # 私有屬性的修改是沒法生效的,由於其私有屬性的key和類名相關, self.age=10 #覆蓋共有屬性,其以前的age是0,如今覆蓋成10,由於實例的字典中的key是惟一的。此處由於在a.__init__(self,name)以後定義,所以其會 # 覆蓋以前name傳入的值 def shout(self): print ('B shout') x=B('jerry',30) # 此處傳入的值是30,因爲下面的覆蓋,變成了10 x.shout() print (x.__dict__) #其私有屬性的_A__name 仍然存在,_B__name 也存在, print (x.getname)
結果以下
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name='tom',age=0): self.__name=name # 此處是實例的初始化函數,固然要在其實例化後才能表現出該屬性,該類中也找不到,固然在其子類中也不能找到 self.age=age @property def getname(self): return self.__name def shout(self): print ('A shout') class B(A): x=456 def __init__(self,name,age): self.age=age # 此處的傳入後會修改爲50,由於其是在修改以後傳入A類中進行處理的 A.__init__(self,name,age) #經過父類的調用來調用父類的屬性,此中可調用父類的私有屬性, self.__name=name+" "+'B' # 私有屬性的修改是沒法生效的,由於其私有屬性的key和類名相關, # self.age=10 #覆蓋共有屬性,其以前的age是0,如今覆蓋成10,由於實例的字典中的key是惟一的。此處由於在a.__init__(self,name)以後定義,所以其會 # 覆蓋以前name傳入的值 def shout(self): print ('B shout') x=B('jerry',30) # 此處傳入的值是30,因爲下面的覆蓋,變成了10 x.shout() print (x.__dict__) #其私有屬性的_A__name 仍然存在,_B__name 也存在, print (x.getname)
結果以下
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name='tom',age=10): self.name=name self.__age=age def shout(self): print ('A shount') @property def getage(self): return self.__age class B(A): x=456 def __init__(self,name,age): # 子類的初始化 self.name=name # 此處進行覆蓋共有屬性,其私有屬性未處理 def shout(self): print ('B shout') b=B('jerry',30) print (b.__dict__) # 此處只獲取到了從新定義的name,而私有屬性__age 不存在
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name='tom',age=10): self.name=name self.__age=age def shout(self): print ('A shount') @property def getage(self): return self.__age class B(A): x=456 def __init__(self,name,age): # 子類的初始化 self.__age=age # 此處進行覆蓋私有屬性,其私有屬性未處理 def shout(self): print ('B shout') b=B('jerry',30) print (b.__dict__) # 此處只獲取到了從新定義的__age,而共有屬性name則不存在
結果以下
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name='tom',age=10): self.name=name self.__age=age def shout(self): print ('A shount') @property def getage(self): return self.__age class B(A): x=456 def __init__(self,name,age): # 子類的初始化 A.__init__(self,name,age) #調用父類方法,處理獲取屬性 def shout(self): print ('B shout') b=B('jerry',30) print (b.__dict__) # 此處可獲取父類中的共有屬性和私有屬性 print (b.name,b._A__age) # 已經進行了修改,,由於上面的def __init__(self,name,age): 中的age在實例化後已經被修改 print (b.getage)
結果以下
#!/usr/bin/poython3.6 #conding:utf-8 class A: x=123 def __init__(self,name='tom',age=10): self.name=name self.__age=age def shout(self): print ('A shount') @property def getage(self): return self.__age class B(A): x=456 def __init__(self,name,age): # 子類的初始化 A.__init__(self,name,age) #調用父類方法,處理獲取屬性 self.__age=age # 重覆蓋age,但沒法完成,由於其私有屬性和類名有關,其至關於增長 def shout(self): print ('B shout') b=B('jerry',30) print (b.__dict__) # 此處可獲取父類中的共有屬性和私有屬性 print (b.name,b._A__age) # 已經進行了修改,,由於上面的def __init__(self,name,age): 中的age在實例化後已經被修改 print (b.getage)
結果以下
#!/usr/bin/poython3.6 #conding:utf-8 class A: @classmethod def class_method(cls): print (cls.__name__) # 此處打印類的名稱 @staticmethod # 此處定義其靜態方法 def static_method(): print ('A staticmethod') class B(A): @classmethod def class_method(cls): print (cls.__name__) @staticmethod def static_method(): print ('B staticmethod') class C(B): pass b=C() b.class_method() # 此處由於調用的是C類,所以其cls.__name__打印的是C而並不是是B b.static_method() # 此處的靜態方法是B,由於C中未定義靜態方法 print (B.__dict__) a=A() a.class_method() # 此處由於調用的時A類,所以cls.__name__打印的是A a.static_method() print (A.__dict__)
結果以下
靜態方法會進行屬性查找,找到及截至,類方法由於類的繼承而進入該C類中,而C類調用其類方法後獲得的名稱固然是C本身,此處若只有A配置類屬性,則B和C進行調用,則各自cls.name 仍然是各個實例的類,至關於本身在調用本身的類。此處大大的複用了,此處就是多態
#!/usr/bin/poython3.6 #conding:utf-8 class A: def __init__(self,name,age): self.name=name self.age=age def __getname(self): #此處定義私有方法 return self.name class B(A): def __init__(self,name,age): # 此處進行初始化,方法不能繼承 self.name=name def __getname(self): # 此處屬於重定義私有方法 return self.age b=B('tom',30) print (b.__dict__) print (B.__dict__) a=A('jerry',20) print (a.__dict__) print (A.__dict__) # 私有方法是屬於類的,不能被繼承,
結果以下
原則: OCP 原則,多繼承,少修改
繼承的用途: 加強基類,實現多態多態:在面向對象環境中,父類,子類經過繼承聯繫到一塊兒,若是能夠經過一套方法,就能夠實現不一樣表現,這就是多態
一個類繼承多個類就是多繼承,它具備多個類的特徵
多態是繼承+多態
多態的弊端:
1 多繼承很好的模擬了世界,由於事物不多是單一繼承,可是捨棄簡單,必然引入複雜性,帶來了衝突
多繼承的實現會致使編譯器設計的複雜度增長,因此不少語言都捨棄了類的多繼承。多繼承可能帶來二義性
解決方案
實現多繼承的語言,須要解決二義性和,深度優先或廣度優先
左邊是多繼承,右邊是單繼承
多繼承帶來的路徑選擇問題
python 使用MRO(method resolution order)解決了基類搜索順序問題
歷史緣由,MRO有三個搜索算法:
1 經典算法,按照定義從左到右,深度優先策略,2.2以前
左圖的MRO是MyClass,D,B,A,C,A
新式類算法,經典算法的升級,重複的只保留最後一個2.2
左圖的MRO是MyClass,D,B,C,A,object
C3 算法,在類被建立出來的時候,就計算出一個MRO有序列表,2.3以後,python3惟一支持的算法
左圖中的MRO是MyClass,D,B,C,A,object 的列表
C3 算法解決的多繼承的二義性
多繼承的缺點
當類不少時,繼承複雜的狀況下,繼承路徑太多,很難說清什麼樣的繼承路徑
Python 語法是容許多繼承,但python代碼是解釋器執行的,只有執行到的時候,才發現錯誤
團隊協做開發,若是引入多繼承,代碼將不可控
無論編程語言是否支持多繼承,都應當避免多繼承
python2.x 裏面支持經典類和新式類
python3.x 裏面僅支持新式類
經典類,其能夠不寫父類。
新式類,其若是沒有繼承的父類則直接寫object,必須寫父類,若是有父類,則直接寫父類
只有新式類支持mro() 方法
對於新式類,是廣度優先
對於經典類,是深度優先
對於新式類,當調用D實例時,其結果是執行D的輸出,當D中爲pass 佔位,其繼承了B的輸出,當B中的結果爲pass 時,其會繼承C中的輸出,
對於經典類,當調用D實例時,其結果是執行D的輸出,當D中爲pass 佔位,其繼承了B的輸出,當B中的結果爲pass 時,其會繼承A中的輸出,由於B繼承了A,由於其是深度優先。
繼承時,共有的,子類和實例均可以隨意訪問,私有成員被隱藏,子類和實例不可直接訪問,當私有變量所在的類內的方法中能夠訪問這個私有變量,python經過本身一套實現,實現和其餘語言同樣的面向對象的繼承機制
屬性查找順序
實例的_dict_-----類的_dict_ -----父類的_dict_,若是搜索到這些地方後沒有找到就會拋異常,先找到就當即返回了。
裝飾器: 用裝飾器裝飾一個類,把功能給類附加上去,那個類須要,就裝飾它
Mixin
文檔Document 類是其餘全部文檔類的抽象基類,
Word,PDF類是Document的子類。
此處可抽象爲兩種類
1 共同的特性抽象成一個類,父類
2 其餘不一樣的特性組成其餘的相關的屬性,子類
Mxin 本質上是多繼承實現的
mixin 提現的是一種組合的設計模式
在面向對象的設計中,一個複雜的類,每每須要不少功能,而這些功能有來自不一樣的類提供,這些就須要不少組合在一塊兒。
MIxin 類的使用原則:
Mixin 類中不該該顯式的出現__init__初始化方法
Mixin 類一般不能獨立工做,由於它是準本混入別的類中的部分功能實現
Mixin 類的祖先類也應該是Mixin類
Mixin類和裝飾器
這兩種方式都是可使用,看我的喜愛
若是須要繼承就使用MixIn類的方法
1 子類中欲實現本身的功能本身處理,再也不父類中進行定義
#!/usr/local/bin/python3.6 #coding:utf-8 class Document: # 父類 def __init__(self,content): self.content=content def print(self): # 共有的屬性 print (self.content) class Word(Document): pass class PrintableWord(Word): def print(self): # 子類本身的私有屬性 print ("Word pinrt {}".format(self.content)) class Pdf(Document): pass # 子類自定義私有屬性 print (PrintableWord.mro()) word=PrintableWord('test\nabc') word.print()
結果以下
2 經過父類中實現。子類若須要個性化需求,則本身修改
#!/usr/local/bin/python3.6 #coding:utf-8 def printable(cls): #此處傳入的是類函數 def _print(self): # 此處的實例能夠看做是這個類函數的參數 print (self.content,'裝飾器') # 此處是計算的返回值 cls.print=_print # 此處對返回值進行賦值 return cls # 並返回該函數的執行結果 class Document: def __init__(self,content): self.content=content class Word(Document): pass class Pdf(Document): pass @printable class PrintableWord(Word): #此處先繼承,後裝飾 pass print (PrintableWord.mro()) pw=PrintableWord('test string') pw.print()
結果以下
另外一種方式,使用lambda 進行處理
#!/usr/local/bin/python3.6 #coding:utf-8 def printable(cls): # 此處是給對應的類增長相關的屬性,使得其更加健壯 cls.print=lambda self: print (self.content) cls.NAME='name' return cls class Document: def __init__(self,content): self.content=content class Word(Document): pass class Pdf(Document): pass @printable #先繼承,後裝飾 class PrintableWord(Word): pass print (PrintableWord.mro()) pw=PrintableWord('test string') pw.print() print (pw.NAME)
結果以下
#!/usr/bin/poython3.6 #conding:utf-8 class Docment: def __init__(self,content): self.content=content def print(self): print (self.content) class PrintableMixin: def print(self): print ('*'*20) print ('Pdf print {}'.format(self.content)) class Word(Docment): pass class Pdf(Docment): pass class PrintablePdf(PrintableMixin,Pdf): # 此處繼承了Mixin和Pdf兩個類,此處是多繼承,Mixin繼承了object。而Pdf繼承了Docment # 而Docment 繼承了object,此處的做用是使用PrintableMixin覆蓋以前的DOcment纔是要求,通常的Mixin都是在最前面放的 pass print (PrintablePdf.mro()) #查看MRO的輸出,以MRO的輸出爲準 Pdf=PrintablePdf('test\nabc') Pdf.print()
結果以下
調換位置以下
#!/usr/bin/poython3.6 #conding:utf-8 class Docment: def __init__(self,content): self.content=content def print(self): print (self.content) class PrintableMixin: def print(self): print ('*'*20) print ('Pdf print {}'.format(self.content)) class Word(Docment): pass class Pdf(Docment): pass class PrintablePdf(Pdf,PrintableMixin): # 此處繼承了Mixin和Pdf兩個類,此處是多繼承,Mixin繼承了object。而Pdf繼承了Docment # 而Docment 繼承了object,此處的做用是使用PrintableMixin覆蓋以前的DOcment纔是要求,通常的Mixin都是在最前面放的 pass print (PrintablePdf.mro()) #查看MRO的輸出,以MRO的輸出爲準 Pdf=PrintablePdf('test\nabc') Pdf.print()
結果以下:
#!/usr/bin/poython3.6 #conding:utf-8 class Docment: def __init__(self,content): self.content=content def print(self): print (self.content) class PrintableMixin: def print(self): print ('*'*20) print ('Pdf print {}'.format(self.content)) class Word(Docment): pass class Pdf(Docment): pass class SuperPrintableMixin(PrintableMixin): # 此處使用類的繼承來打印一個超類 def print(self): print ('~'*20) super().print() print('~'*20) class PrintablePdf(SuperPrintableMixin,Pdf): pass print (PrintablePdf.mro()) #查看MRO的輸出,以MRO的輸出爲準 Pdf=PrintablePdf('test\nabc') Pdf.print()
結果以下
裝飾器使用的是函數的方式實現的,其函數只能實現封裝功能,而Mixin是一個類,其類可實現封裝,繼承,和多態的特性。