面向過程的程序設計的核心是過程(流水線式思惟),過程即解決問題的步驟,面向過程的設計就比如精心設計好一條流水線,考慮周全何時處理什麼東西。python
優勢是:極大的下降了寫程序的複雜度,只須要順着要執行的步驟,堆疊代碼便可。git
缺點是:一套流水線或者流程就是用來解決一個問題,代碼牽一髮而動全身。程序員
應用場景:一旦完成基本不多改變的場景,著名的例子有Linux內核,git,以及Apache HTTP Server等。面試
面向對象的程序設計的核心是對象(上帝式思惟),要理解對象爲什麼物,必須把本身當成上帝,上帝眼裏世間存在的萬物皆爲對象,不存在的也能夠創造出來。面向對象的程序設計比如如來設計西遊記,如來要解決的問題是把經書傳給東土大唐,如來想了想解決這個問題須要四我的:唐僧,沙和尚,豬八戒,孫悟空,每一個人都有各自的特徵和技能(這就是對象的概念,特徵和技能分別對應對象的屬性和方法),然而這並很差玩,因而如來又安排了一羣妖魔鬼怪,爲了防止師徒四人在取經路上被搞死,又安排了一羣神仙保駕護航,這些都是對象。而後取經開始,師徒四人與妖魔鬼怪神仙互相纏鬥着直到最後取得真經。如來根本不會管師徒四人按照什麼流程去取。算法
優缺點編程
優勢是:解決了程序的擴展性。對某一個對象單獨修改,會馬上反映到整個體系中,如對遊戲中一我的物參數的特徵和技能修改都很容易。編程語言
缺點:可控性差,沒法向面向過程的程序設計流水線式的能夠很精準的預測問題的處理流程與結果,面向對象的程序一旦開始就由對象之間的交互解決問題,即使是上帝也沒法預測最終結果。因而咱們常常看到一個遊戲人某一參數的修改極有可能致使陰霸的技能出現,一刀砍死3我的,這個遊戲就失去平衡。ide
應用場景:需求常常變化的軟件,通常需求的變化都集中在用戶層,互聯網應用,企業內部軟件,遊戲等都是面向對象的程序設計大顯身手的好地方。函數
在python 中面向對象的程序設計並非所有。加密
面向對象編程可使程序的維護和擴展變得更簡單,而且能夠大大提升程序開發效率 ,另外,基於面向對象的程序可使它人更加容易理解你的代碼邏輯,從而使團隊開發變得更從容。
python中一切皆爲對象,類型的本質就是類
類: 具備相同屬性和相同動做的一類事物,組成一個類
對象:具備的摸一個具備是技術性和具體動做的一個實體
相似抽象的,對象是具體的
類被創造出來,是爲了描述對象的
函數版本的代碼是面向過程的思想,而類的引入則是面向對象的思想了。你先規劃好有哪些角色,角色有哪些相同的屬性,有哪些不能的能力,有哪些不一樣的屬性,先作好規劃。而後去一一實現
class Person: #定義一我的類 role = 'person' #人的角色屬性都是人 # 靜態屬性 def walk(self): #人均可以走路,也就是有一個走路方法,也叫動態屬性 print("person is walking...")
只要是寫在類名中的名字,無論是變量仍是函數名,都不能在類的外部直接調用
只能經過類名來使用它
class Person: #定義一我的類 role = 'person' #人的角色屬性都是人 def walk(self): #人均可以走路,也就是有一個走路方法 print("person is walking...") print(Person.role) #查看人的role屬性 print(Person.walk) #引用人的走路方法,注意,這裏不是在調用
class 類名: 靜態屬性 = 123 def 動態屬性(self): # 在類中的方法的一個默認的參數,但也只是一個形式參數,約定必須叫self print('-->',self) # 只要是寫在類名中的名字 無論是變量仍是函數名 都不能在類的外部直接調用 # 只能經過類名來使用它 # 類名的第一個功能是 —— 查看靜態屬性 print(類名.靜態屬性) # 查看 類名.靜態屬性 = 456 # 修改 print(類名.靜態屬性) 類名.靜態屬性2 = 'abc'# 增長 print(類名.靜態屬性2) # del 類名.靜態屬性2 # print(類名.靜態屬性2) print(類名.__dict__) # 類中必要的默認值以外 還記錄了程序員在類中定義的全部名字
實例化的過程理解
爲何會執行init中的內容?
self究竟是什麼?
實例化的過程
類名()就是實例化
在實例化的過程當中 發生了不少事情是外部看不到的
1.建立了一個對象
2.自動調用__init__方法
這個被創造的對象會被當作實際參數傳到__init__方法中,而且傳給第一個參數self
3.執行init方法中的內容
4.自動的把self做爲返回值 返回給實例化的地方
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對象,有本身具體的名字、攻擊力和生命值。
語法:對象名 = 類名(參數)
alex = Person('alex') #類名()就等於在執行Person.__init__() #執行完__init__()就會返回一個對象。這個對象相似一個字典,存着屬於這我的自己的一些屬性和方法。
print(alex.name) #查看屬性直接 對象名.屬性名 print(alex.walk()) #調用方法,對象名.方法名()
self:在實例化時自動將對象/實例自己傳給__init__的第一個參數,你也能夠給他起個別的名字,可是正常人都不會這麼作。
由於你瞎改別人就不認識
一:咱們定義的類的屬性到底存到哪裏了?有兩種方式查看 dir(類名):查出的是一個名字列表 類名.__dict__:查出的是一個字典,key爲屬性名,value爲屬性值 二:特殊的類屬性 類名.__name__# 類的名字(字符串) 類名.__doc__# 類的文檔字符串 類名.__base__# 類的第一個父類(在講繼承時會講) 類名.__bases__# 類全部父類構成的元組(在講繼承時會講) 類名.__dict__# 類的字典屬性 類名.__module__# 類定義所在的模塊 類名.__class__# 實例對應的類(僅新式類中)
回到人狗大戰,如今須要對咱們的類作一點改變。
人類除了能夠走路以外嗎,還應該具有一些攻擊技能
class Person: # 定義一我的類 role = 'person' # 人的角色屬性都是人 def __init__(self,name,sex,hp,dps): self.name = name self.sex = sex self.hp = hp self.dps = dps def attack(self,dog): # 人能夠攻擊狗,這裏的狗也是一個對象 # 人攻擊狗,那麼狗的生命值就壺根據人的攻擊力而降低 dog.hp -= self.dps print('%s打了%s,%s掉了%s點血,剩餘%s點血' % (self.name, dog.name, dog.name, self.dps, dog.hp))
print(alex.name) print(ha2.name) print(ha2.dps)
你也能夠引用一個方法,由於方法也是一個屬性,只不過是一個相似函數的屬性,咱們也管它叫動態屬性。
引用動態屬性並非執行這個方法,要想調用方法和調用函數是同樣的,都須要在後面加上括號
Person.attack(alex,ha2) ## 調用類中的方法的方式 能夠簡寫下一行爲 alex.attack alex.attack(ha2) ha2.bite(alex)
class Circle(): # 定義一個類--圓 def __init__(self,r): self.pi = 3.14 self.r = r def area(self): print('area:',self.pi * self.r ** 2) def perimeter(self): print('perimeter:',self.pi * self.r * 2) r = Circle(5) # 實例化一個半徑=5的圓 r.area() r.perimeter()
class 類名: def __init__(self,參數1,參數2) self.對象的屬性1 = 參數1 self.對象的屬性2 = 參數2 def 方法名(self): pass def 方法名2(self): pass 對象名 = 類名(參數1,參數2) # 對象就是實例,表明一個具體的東西 # 類名() 類名加括號就是實例化一個類,至關於調用了__init__方法 # 括號裏有參數,參數裏不須要穿self,其餘與__init__找那個的形參一一對應。 # 結果返回一個對象 對象名.對象屬性1 # 查看對象的屬性,直接用對象名.屬性名 便可 對象名.方法名() # 調用類中的方法。直接用 對象名.方法名() 便可
建立一個類就會建立一個類的Name Space,用來存儲類中定義的全部名字,這些名字稱爲類的屬性
而類有兩種屬性:靜態屬性和動態屬性
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.name會先從obj本身的名稱空間裏找name,找不到則去類中找,類也找不到就找父類...最後都找不到就拋出異常
class Person: COUNTRY = '中國人' def __init__(self,name): self.name = name alex = Person('alex') egon = Person('egon')
實例化過程:
當實例化一個對象的時候,在內存中間的建立步驟:
1. 在內存中開闢一塊地址,存放類的名稱,同時開闢類的namespace存放類中的靜態屬性和動態屬性
2. 實例化執行的時候,執行init,開闢一個新的內存空間,self指向這個空間,在裏面存放 name='alex'
3. self.name = name 賦值過程
4. alex = Person('alex') # 將對象賦值給alex
class Person: COUNTRY = '中國人' # 靜態屬性 def __init__(self,name): self.name = name def eat(self): print('%s在吃泔水'%self.name) alex = Person('alex') egon = Person('egon') print(alex.name) print(egon.name) print(alex.COUNTRY) alex.eat() # Person.eat(alex) alex ---> Person 當一個類在建立一個實例的時候 就產生了一個這個實例和類之間的聯繫 (類對象指針) 能夠經過實例 對象 找到實例化它的類 可是 類不能找到它的實例化
class Person: COUNTRY = ['中國人'] # 靜態屬性 Country = '中國人' # 靜態屬性 def __init__(self,name): self.name = name def eat(self): print('%s在吃泔水'%self.name) alex = Person('alex') egon = Person('egon') print(alex.Country) alex.Country = '印度人' print(alex.Country) print(egon.Country) print(Person.Country) alex.COUNTRY[0] = '印度人' print(alex.COUNTRY) print(egon.COUNTRY) print(Person.COUNTRY) alex.COUNTRY = ['印度人'] print(egon.COUNTRY) print(Person.COUNTRY)
# 在訪問變量的時候,都先使用本身命名空間中的,若是本身的空間中沒有,再到類的空間中去找
# 在使用對象修改靜態變量的過程當中,至關於在本身的空間中建立了一個新的變量
# 在類的靜態變量的操做中 應該使用類名來直接進行操做 就不會出現烏龍問題
建立一個類,可以自動及素顏這個類建立了多少個實例
class Foo: count = 0 def __init__(self): ###每次實例化一個對象,都會自動調用一次__init__函數 Foo.count += 1 f1 = Foo() print(Foo.count) [Foo() for i in range(10)] print(Foo.count)
結論:
對象能夠訪問類的命名空間
類不能訪問對象的命名空間
軟件重用的重要方式除了繼承以外還有另一種方式,即:組合
組合指的是,在一個類中以另一個類的對象做爲數據屬性,稱爲類的組合
一切皆對象
數據類型 -- 都是類
那麼任意一個字符串,都是str這個類的對象
人狗大戰-- 升級版
class Person: def __init__(self,name,sex,hp,dps): self.name = name self.hp = hp self.dps = dps self.sex = sex self.bag = [] def attack(self,dog): dog.hp -= self.dps print('%s打了%s,%s掉了%s點血,剩餘%s點血' % (self.name, dog.name, dog.name, self.dps, dog.hp)) class Dog: def __init__(self,name,kind,hp,dps): self.name = name self.hp = hp self.dps = dps self.kind = kind def bite(self,person): person.hp -= self.dps print('%s打了%s,%s掉了%s點血,剩餘%s點血' % (self.name, person.name, person.name, self.dps, person.hp)) class Weapon: def __init__(self,name,price,dps): self.name = name self.price = price self.dps = dps def kill(self,dog): dog.hp -= self.dps alex = Person('alex','N/A',250,5) ha2 = Dog('哈士奇','藏獒',15000,200) roubaozi = Weapon('肉包子',600000,10000) alex.money = 1000000 if alex.money >= roubaozi.price: alex.weapon = roubaozi #把weapon類的肉包子對象座位對象alex的屬性。
alex.weapon.kill(ha2) # <==> roubaozi.kill
print(ha2.hp)
pi = 3.14 class Circle: def __init__(self,r): self.r = r def area(self): return pi * self.r ** 2 def perimeter(self): return self.r *pi * 2 # r1 = Circle(2) # # print(r1.area()) class Ring(): def __init__(self,out_r,in_r): self.out_c = Circle(out_r) ## 把圓的實例化過程放在init中作初始化 在這裏初始化了兩個圓出來 self.in_c = Circle(in_r) def area(self): return self.out_c.area() - self.in_c.area() # 這兩個圓調用area方法 相減! def perimeter(self): return self.out_c.perimeter() - self.in_c.perimeter() ring1 =Ring(5,2) print(ring1.area()) print(ring1.perimeter())
繼承是一種建立新類的方式,在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'>,)
抽象即抽取相似或者說比較像的部分。
抽象分紅兩個層次:
1.將奧巴馬和梅西這倆對象比較像的部分抽取成類;
2.將人,豬,狗這三個類比較像的部分抽取成父類。
抽象最主要的做用是劃分類別(能夠隔離關注點,下降複雜度)
繼承:是基於抽象的結果,經過編程語言去實現它,確定是先經歷抽象這個過程,才能經過繼承的方式去表達出抽象的結構。
抽象只是分析和設計的過程當中,一個動做或者說一種技巧。經過抽象能夠獲得類。
使用代碼能夠解決代碼重用的問題
==========================第一部分 例如 貓能夠:喵喵叫、吃、喝、拉、撒 狗能夠:汪汪叫、吃、喝、拉、撒 若是咱們要分別爲貓和狗建立一個類,那麼就須要爲 貓 和 狗 實現他們全部的功能,僞代碼以下: #貓和狗有大量相同的內容 class 貓: def 喵喵叫(self): print '喵喵叫' def 吃(self): # do something def 喝(self): # do something def 拉(self): # do something def 撒(self): # do something class 狗: def 汪汪叫(self): print '汪汪叫' def 吃(self): # do something def 喝(self): # do something def 拉(self): # do something def 撒(self): # do something ==========================第二部分 上述代碼不難看出,吃、喝、拉、撒是貓和狗都具備的功能,而咱們卻分別的貓和狗的類中編寫了兩次。若是使用 繼承 的思想,以下實現: 動物:吃、喝、拉、撒 貓:喵喵叫(貓繼承動物的功能) 狗:汪汪叫(狗繼承動物的功能) 僞代碼以下: class 動物: def 吃(self): # do something def 喝(self): # do something def 拉(self): # do something def 撒(self): # do something # 在類後面括號中寫入另一個類名,表示當前類繼承另一個類 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 cry(self): print('喵喵叫') class Dog(Animal): def __init__(self, name): self.name = name self.breed='狗' def cry(self): print('汪汪叫') # ######### 執行 ######### c1 = Cat('小白家的小黑貓') c1.eat() c2 = Cat('小黑的小白貓') c2.drink() d1 = Dog('胖子家的小瘦狗') d1.eat() 使用繼承來重用代碼比較好的例子 使用繼承來解決代碼重用的例子
在開發程序的過程當中,若是咱們定義了一個類A,而後又想新創建另一個類B,可是類B的大部份內容與類A的相同時
咱們不可能從頭開始寫一個類B,這就用到了類的繼承的概念。
經過繼承的方式新建類B,讓B繼承A,B會‘遺傳’A的全部屬性(數據屬性和函數屬性),實現代碼重用
回頭看咱們的人狗大戰代碼中,人和狗都有同時擁有的屬性,大量代碼重複。那麼咱們能夠把相同的屬性抽象出來寫一個父類Animal。
而後人和狗分別繼承animal的屬性,再分別加入本身的屬性。
若是子類沒有init方法:
若是子類有本身的init方法呢?
若是有本身的init就不回去父類的命名空間去找init方法。
若是仍舊想引用父類的init,就須要用super() 的寫法去引用父類的init方法。
# class Animal(): def __init__(self, name, hp, dps): self.name = name self.hp = hp self.dps = dps def eat(self): print('%s吃藥回血了' %self.name) class Person(Animal): def __init__(self, name, hp, dps, sex): Animal.__init__(self, name, hp, dps) # 當寫成super()的時候,不須要再寫self參數 self.sex = sex def attack(self, dog): dog.hp -= self.dps print('%s打了%s,%s掉了%s點血,剩餘%s點血' % (self.name, dog.name, dog.name, self.dps, dog.hp)) class Dog(Animal): def __init__(self, name, hp, dps, kind): super().__init__(name, hp, dps) # 當寫成super()的時候,不須要再寫self參數 self.kind = kind def bite(self, person): person.hp -= self.dps print('%s咬了%s,%s掉了%s點血,剩餘%s點血' % (self.name, person.name, person.name, self.dps, person.hp)) alex = Person('alex',250, 5, '不詳') ha2 = Dog('哈士奇', 15000, 200, '藏獒') alex.eat() ha2.eat() ha2.bite(alex) alex.attack(ha2)
一道著名的面試題: 下面代碼的輸出是什麼?
class Foo: def __init__(self): self.func() def func(self): print('in Foo') class Son(Foo): def func(self): print('in Son') Son()
答案是: in Son
在涉及到繼承的時候,見到self,不要在本類中找它的調用。而要先看看,self究竟是誰。
在這道題中。self是初始化Son的時候執行的,因此調用self.func的時候,執行的是Son中的func函數。
python兩種類
經典類 py3已經滅絕了 在python2裏還存在,在py2中只要程序員不主動繼承object,這個類就是經典類 —— 深度優先
新式類 python3全部的類都是新式類,全部的新式類都繼承自object —— 在多繼承中遵循廣度優先算法
鑽石繼承問題
python3
廣度優先算法--遍歷算法
鑽石模型和小烏龜模型
class A: def f(self): print('in A') class B(A): pass # def f(self): # print('in B') class C(A): pass # def f(self): # print('in C') class D(B,C): pass # def f(self): # print('in D') class E(C): pass # def f(self): # print('in B') class F(D,E): pass # def f(self): # print('in C') d = D() d.f() print(F.mro()) class A: def f(self): print('in A') class B(A): def f(self): print('in B') super().f() class C(A): pass def f(self): print('in C') super().f() class D(B,C): def f(self): print('in D') super().f() d = D() d.f()
# super和找父類這件事是兩回事
# 在單繼承中 super就是找父類
# 在多級承中 super的軌跡 是根據整個模型的起始點而展開的一個廣度優先順序 遵循mro規則
多態指的是一類事物有多種形態
動物有多種形態:人,狗,豬
在python中,到處都是多態。
多態性
peo=People() dog=Dog() pig=Pig() #peo、dog、pig都是動物,只要是動物確定有talk方法 #因而咱們能夠不用考慮它們三者的具體是什麼類型,而直接使用 peo.talk() dog.talk() pig.talk() #更進一步,咱們能夠定義一個統一的接口來使用 def func(obj): obj.talk()
廣義上的封裝 :把變量和函數都放在類中
狹義上的封裝 :把一些變量 或者 方法 隱藏起來,不對外公開
公有的 :
私有的 : __名字
class Person: __country = '中國' # 私有的靜態屬性 print(Person.__country) # AttributeError: type object 'Person' has no attribute '__country' # 私有的名字 只能在類的內部使用 不能在類的外部使用 print(Person.__dict__) # _Person__country print(Person._Person__country) # 不能使用上面這種方式去調用私有的變量 # 若是非要在類的外部調用一個私有的名字,name必須是在私有的名字前面加 _類名__私有的名字 Person.__name = 'XXX' print(Person.__name) # 在類的外部不能第一一個私有變量 print(Person.__dict__)
# 私有的變量 :
# 在類的內部 若是使用__變量的形式會發生變形,python會自動的爲你加上_類名
class Person: __country = '中國' def __init__(self,name,pwd): self.name = name self.__pwd = pwd # 私有的對象屬性 def login(self): print(self.__dict__) if self.name == 'alex' and self.__pwd == 'alex3714': print('登陸成功') alex = Person('alex','alex3714') alex.login() print(alex.__dict__) #{'name': 'alex', '_Person__pwd': 'alex3714'} print(alex.name) print(alex.__pwd) # 報錯 AttributeError: 'Person' object has no attribute '__pwd'
class Person: def __init__(self):pass def __製造密碼轉換(self,inp): ## 這個製造的密碼,是不但願提供給外部使用的 print('eating') def 註冊(self): inp = input('pwd>>>') 加密以後的密碼 = self.__製造密碼轉換(inp)
靜態屬性 、 對象屬性、 方法(動態屬性) 前面加上雙下劃綫都會變成私有的
私有的特色就是隻能在類的內部調用,不能在類的外部使用
肯定下面兩段代碼 輸出內容是什麼 class Foo: def __init__(self): self.func() def func(self): print('in Foo') class Son(Foo): def func(self): print('in son') s = Son() # in son # 實例化一個對象的時候,把son這個對象傳給__init__作初始化。可是Son沒有init,回去找父類的init。在這個init中調用了self.func函數,由於self是指向實例的,因此仍舊是Son中的函數被執行
class Foo: def __init__(self): self.__func() # self._Foo__func def __func(self): print('in Foo') class Son(Foo): def __func(self): # _Son__func 只是加載到內存了,沒有調用 print('in son') s = Son() # in Foo
# 解析 當調用Son的時候實例化一個對象,會先執行初始化,__init__函數 ,在這個函數中調用的__func。 在哪一個地方指向__方法 就會自動拼接 _類名__方法名。因此拼接出來,被調用的是self._Foo__func。