面向過程的程式設計經常用於操作系統的內核,git等,一成不變的流水線式解決一個問題,極大程度降低程式複雜性。 面向對象的程式設計解決了程式的擴展性(類可產生各種各樣的對象,對於新增技能或修改技能可使用方法直接調用),但是可控性差,因為面向對象程式一旦開始就是由對象之間交互來解決問題。 OOD面向對象 ...
面向過程的程式設計經常用於操作系統的內核,git等,一成不變的流水線式解決一個問題,極大程度降低程式複雜性。
面向對象的程式設計解決了程式的擴展性(類可產生各種各樣的對象,對於新增技能或修改技能可使用方法直接調用),但是可控性差,因為面向對象程式一旦開始就是由對象之間交互來解決問題。
OOD面向對象的程式設計
先找程式中所有的對象,將對象歸納出類(並歸納出共同屬性與方法和不同的屬性)。
OOP面向對象編程
編程時先定義出類,再根據類實例化出對象。
python3統一了類與類型的概念,python3中的類型就是類。
編程方式:
面向過程: 根據代碼在腳本的堆疊順序,從上到下依次執行,
函數式編程:將相同功能的代碼封裝到函數中,直接調用即可,減少代碼重覆性,
面向對象:對函數進行分類和封裝,將同類的函數放到一個類中,使調用更簡單。
類的簡介
類和對象
類就是一個模板,模板里可以包含多個方法(即函數),方法里實現一些功能,對象則是根據模板創建的實例,通過實例對象可以執行類中的函數。

#創建類 class+類名 class foo: #class是關鍵字,表示類,foo是類的名字 def f1(self): #類的方法1 pass def f2(self): #類的方法2 pass #創建對象 對象 = 類名() bar = foo() #創建一個bar對象 ,此對象中有類中所有的方法 ,創建對象,類名稱後加括弧即可 #調用對象的方法 對象.方法名() bar.f1() bar.f2()創建類和變數的方式
類名+()就等於在執行Person.__init__(),執行完__init__()就會返回一個對象。這個對象類似一個字典,存著屬於這個人本身的一些屬性和方法。
這裡提一嘴特殊的類屬性。
類名.__name__# 類的名字(字元串)
類名.__doc__# 類的文檔字元串
類名.__base__# 類的第一個父類
類名.__bases__# 類所有父類構成的元組
類名.__dict__# 查看類和對象的名稱空間
類名.__module__# 類定義所在的模塊
類名.__class__# 實例對應的類(僅新式類中)
python中使用class定義類,在python3中只有新式類(預設繼承object),而python2中有新式類(手寫繼承object)與經典類的區別,可使用__basic__查看繼承關係。
python2的經典類:
類的一般操作形式:
class Teacher: #定義類 x=1 #調用屬性時不論對象還是類指向的都是同一塊記憶體地址,屬性又稱靜態欄位 def __init__(self,name,sex,age,money):#init不能有返回值,裡面放的是獨有特征,共有特征使用單獨函數定義 self.name=name #普通欄位 self.sex=sex self.age=age self.money=money def search(self):#teacher調用函數或者實例化對象的綁定方法調用的不是同一塊記憶體地址,普通方法 print("scord") def study(self): print("study") print(Teacher.x) #1 t=Teacher('jeff','male','110',10)#實例化出來的對象 print(t.name) #jeff print(t.age) #110 t.study() #study Teacher.y=5 print(Teacher.y)#5

class 類名: 類屬性 = None def __init__(self,參數1,參數2): self.對象的屬性1 = 參數1 self.對象的屬性2 = 參數2 def 方法名(self):pass def 方法名2(self):pass 對象名 = 類名(1,2) #對象就是實例,代表一個具體的東西 #類名() : 類名+括弧就是實例化一個類,相當於調用了__init__方法 #括弧里傳參數,參數不需要傳self,其他與init中的形參一一對應 #結果返回一個對象 對象名.對象的屬性1 #查看對象的屬性,直接用 對象名.屬性名 即可 對象名.方法名() #調用類中的方法,直接用 對象名.方法名() 即可 #對象增加屬性 對象.新的屬性名 = 1000通用的class定義方法
類是整個單獨的名稱空間,定義類也就是單獨定義了他的名稱空間(變數,函數和類名字)用來存儲類中定義的所有名字,這些名字稱為類的屬性,靜態屬性就是直接在類中定義的變數,動態屬性就是定義在類中的方法。可以使用__dict__查看類和對象的名稱空間。實例化對象會先從自己的dict查找變數,找不到就去類(父類)的dict找,沒有則會報錯。
類可以進行實例化,可以進行屬性引用。對象只能屬性引用。對象本身只能引用屬性(變數name,sex,age。。。)。
t.name2='tom'#增 del t.name2#刪 t.name='jerry'#改 print(t.name)#查
實例化對象之間的交互:

class Riven: camp='Noxus' #所有玩家的英雄(銳雯)的陣營都是Noxus; def __init__(self,nickname,aggressivity=54,life_value=414): #英雄的初始攻擊力54; self.nickname=nickname #為自己的銳雯起個別名; self.aggressivity=aggressivity #英雄都有自己的攻擊力; self.life_value=life_value #英雄都有自己的生命值; def attack(self,enemy): #普通攻擊技能,enemy是敵人; enemy.life_value-=self.aggressivity #根據自己的攻擊力,攻擊敵人就減掉敵人的生命值。 class Garen: camp='Noxus' #所有玩家的英雄(銳雯)的陣營都是Noxus; def __init__(self,nickname,aggressivity=54,life_value=414): #英雄的初始攻擊力54; self.nickname=nickname #為自己的銳雯起個別名; self.aggressivity=aggressivity #英雄都有自己的攻擊力; self.life_value=life_value #英雄都有自己的生命值; def attack(self,enemy): #普通攻擊技能,enemy是敵人; enemy.life_value-=self.aggressivity #根據自己的攻擊力,攻擊敵人就減掉敵人的生命值。 #對象之間的交互 r1=Riven('芮雯雯') g1=Garen('草叢輪') print(r1.life_value) g1.attack(r1) print(r1.life_value)德瑪西亞大戰瑞文
私有屬性
特點:
類中定義的__x只能在內部使用,如self.__x,引用的就是變形的結果。
這種變形其實正是針對外部的變形,在外部是無法通過__x這個名字訪問到的。
在子類定義的__x不會覆蓋在父類定義的__x,因為子類中變形成了:_子類名__x,而父類中變形成了:_父類名__x,即雙下滑線開頭的屬性在繼承給子類時,子類是無法覆蓋的。
註意:
這種機制也並沒有真正意義上限制我們從外部直接訪問屬性,知道了類名和屬性名就可以拼出名字:_類名__屬性,然後就可以訪問了,如a._A__N。
class people: __name = 'jeff' __age = 12 def set(self): print(self.__name) print(self.__age) p = people() #print(p.__name,p.__age) #AttributeError: 'people' object has no attribute '__name' p.set() 運行結果; jeff 12
封裝在於明確區分內外,使得類實現者可以修改封裝內的東西而不影響外部調用者的代碼;而外部使用用者只知道一個介面(函數),只要介面(函數)名、參數不變,使用者的代碼永遠無需改變。

#類的設計者,輕鬆的擴展了功能,而類的使用者完全不需要改變自己的代碼 class Room: def __init__(self,name,owner,width,length,high): self.name=name self.owner=owner self.__width=width self.__length=length self.__high=high def tell_area(self): #對外提供的介面,隱藏內部實現,此時我們想求的是體積,內部邏輯變了,只需求修該下列一行就可以很簡答的實現,而且外部調用感知不到,仍然使用該方法,但是功能已經變了 return self.__width * self.__length * self.__high #對於仍然在使用tell_area介面的人來說,根本無需改動自己的代碼,就可以用上新功能 r1.tell_area()#對於用戶只要知道這個介面的功能就可以了擴展性
property屬性
把綁定方法裝飾的像一個屬性一樣調用,被property裝飾的屬性會優先於對象的屬性被使用。

class People: def __init__(self,name,weight,height): self.name=name self.weight=weight self.height=height @property def bmi(self): return self.weight / (self.height**2) p1=People('egon',75,1.85) print(p1.bmi)property屬性

import math class Circle: def __init__(self,radius): #圓的半徑radius self.radius=radius @property def area(self): return math.pi * self.radius**2 #計算面積 @property def perimeter(self): return 2*math.pi*self.radius #計算周長 c=Circle(10) print(c.radius) print(c.area) #可以像訪問數據屬性一樣去訪問area,會觸發一個函數的執行,動態計算出一個值 print(c.perimeter) #同上 ''' 輸出結果: 314.1592653589793 62.83185307179586 ''' #註意:此時的特性area和perimeter不能被賦值 c.area=3 #為特性area賦值 ''' 拋出異常: AttributeError: can't set attribute '''但是不能賦值
將一個類的函數定義成特性以後,對象再去使用的時候obj.name,根本無法察覺自己的name是執行了一個函數然後計算出來的,這種特性的使用方式遵循了統一訪問的原則。
對於obj.name我們想要在實際中會有需求能改他的值,實例.name='jeff'就是改self.__name。
這就需要用到@name.setter,刪除特性的方法@name.deleter。

class People: def __init__(self,name,SEX): self.name=name # self.__sex=SEX self.sex=SEX #self.sex='male' p1.sex='male' @property def sex(self): return self.__sex #p1.__sex @sex.setter def sex(self,value): # print(self,value) if not isinstance(value,str): raise TypeError('性別必須是字元串類型') self.__sex=value #p1.__sex='male' @sex.deleter def sex(self): del self.__sex #del p1.__sex p1=People('cobila','male') p1.sex='female' print(p1.sex)特性方法的修改和刪除
類方法
class people: country = 'china' # 類方法,用classmethod來進行修飾 @classmethod def getCountry(cls): #類方法自動將類作為cls傳遞進函數內 return cls.country @classmethod def setCountry(cls, country): cls.country = country p = people() print(p.getCountry()) # 可以用過實例對象引用 print(people.getCountry()) # 可以通過類對象引用 p.setCountry('japan') print(p.getCountry()) 運行結果: china china japan
靜態方法
class people: country = 'china' @staticmethod # 靜態方法使用裝飾器方式綁定,並且作用和普通函數一樣並不會自動傳遞self def getCountry(): return people.country print(people.getCountry()) 運行結果: china
普通方法 預設有一個self對象傳進來,並且只能被對象調用——綁定到對象
類方法 預設有一個cls傳進來表示本類,並且可以被類和對象(不推薦)調用——綁定到類
靜態方法 沒有預設參數,並且可以被類和對象(不推薦)調用——非綁定
面向對象三大特性
封裝
封裝顧名思義就是將東西裝起來然後留出介面供用戶使用,不需要知道內部發生了什麼,只需要知道介面如何調用即可。在此之前所說的__init__定義的普通欄位的賦值,並提供普通方法作為調用介面就是python中的封裝結構。除此之外,私有屬性也是封裝的一種方法之一。
好處:將變化隔離;
便於使用;
提高復用性;
提高安全性;
封裝原則:
將不需要對外提供的內容都隱藏起來;
把屬性都隱藏,提供公共方法對其訪問。
多重封裝:

#創建類 class SQL: def __init__(self,name,passwd): self.name = name self.passwd = passwd def create(self,sql): print(sql) class test: def __init__(self,name,obj): self.name = name self.obj = obj def add(self,arg): print(arg) class test2: def __init__(self,obj): self.obj = obj def iner(slef,arg): print(arg) #創建對象 c1 = SQL('fuzj','12313') c2 = test('aaa',c1) #把c1對象封裝到c2對象里,c2對象會有c1對象的所有方法 c3 = test2(c2) #把c2對象封裝到c3對象中,c3對象會有c2對象的所有方法,同時也就有了c1對象的所有方法 #調用 c1.create("c1調用自身create方法") c2.obj.create('c2調用c1的create方法') c3.obj.add('c3調用c2的add方法') c3.obj.obj.create('c3調用c1的create方法') 結果: c1調用自身create方法 c2調用c1的create方法 c3調用c2的add方法 c3調用c1的create方法多重封裝
繼承
對於面向對象的繼承來說,其實就是將多個類共有的方法提取到父類中,子類僅需繼承父類而不必一一實現每個方法。

class ParentClass1: #定義父類 pass class ParentClass2: #定義父類 pass class SubClass1(ParentClass1): #單繼承,基類是ParentClass1,派生類是SubClass pass class SubClass2(ParentClass1,ParentClass2): #python支持多繼承,用逗號分隔開多個繼承的類 pass繼承
一個類可以繼承多個父類(基類,超類),這點與其他語言略有不同,當類是經典類時,多繼承情況下,會按照深度優先方式查找;當類是新式類時,多繼承情況下,會按照廣度優先方式查找。
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()
#新式類執行bar方法時的查找順序為A --> B --> C --> D

class Hero:#這個英雄類包含了所有英雄都有的屬性和方法 def __init__(self, nickname, aggressivity, life_value): self.nickname = nickname self.aggressivity = aggressivity self.life_value = life_value def attack(self, enemy): enemy.life_value -= self.aggressivity class Garen(Hero):#蓋倫繼承了英雄類 camp='Demacia' def attack(self, enemy): pass def fire(self):#蓋倫獨有的噴火技能 print('%s is firing' %self.nickname) class Riven(Hero): camp='Noxus' g1=Garen('garen',18,200) r1=Riven('rivren',18,200) # print(g1.camp) # print(r1.camp) # g1.fire() g1.attack(g1)瑞文大戰德瑪之繼承

class Animal: #父類 def __init__(self,name,life_value,aggr): self.name = name self.life_value = life_value self.aggr = aggr #攻擊力 def eat(self): self.life_value += 10 class Person(Animal): #子類 def __init__(self,money,name,life_value,aggr): super().__init__(name,life_value,aggr)#super調用父類的構造方法 self.money = money #派生屬性 def attack(self,enemy): #人的派生方法 enemy.life_value -= self.aggr class Dog(Animal): #派生子類 def __init__(self,breed,name,life_value,aggr): #Animal.__init__(self,name,life_value,aggr) #讓子類執行父類的方法,就是父類名.方法名(參數),連self也得傳就是super() super().__init__(name,life_value,aggr) #super關鍵字——新式類 #super(Dog,self).__init__(name,life_value,aggr) #super關鍵字——新式類 self.breed = breed def bite(self,person): #狗的派生方法 person.life_value -= self.aggr def eat(self): # 父類方法的重寫 super().eat() print('dog is eating~~~ ') ha2 = Dog('牛頭梗','旺財',20000,100) print(ha2.life_value) ha2.eat() print(ha2.life_value) # super(Dog,ha2).eat() #調用父類的 print(ha2.life_value)人狗大戰之派生

class Hero: def __init__(self, nickname, aggressivity, life_value): self.nickname = nickname self.aggressivity = aggressivity self.life_value = life_value def attack(self, enemy): print('Hero attack') enemy.life_value -= self.aggressivity # print(Hero.__init__) # print(Hero.attack) class Garen(Hero): camp = 'Demacia' def __init__(self, nickname, aggressivity, life_value, script): Hero.__init__(self,nickname,aggressivity,life_value) # self.nickname = nickname # self.aggressivity = aggressivity # self.life_value = life_value self.script = script def attack(self, enemy): # self=g1,enemy=r1 # self.attack(enemy) #g1.attack() Hero.attack(self, enemy) print('from garen attack') def fire(self): print('%s is firing' % self.nickname) # g1=Garen('garen',18,200) #Garen.__init__(g1,'garen',18,200) g1=Garen('garen',18,200,'人在塔在') #Garen.__init__(g1,'garen',18,200) print(g1.script)繼承更加精簡了代碼
多態
多態即多種形態,在運行時確定其狀態,在編譯階段無法確定其類型,這就是多態。Python中的多態和Java以及C++中的多態有點不同,Python中的變數是弱類型的,在定義時不用指明其類型,它會根據需要在運行時確定變數的類型(個人覺得這也是多態的一種體現),並且Python本身是一種解釋性語言,不進行預編譯,因此它就只在運行時確定其狀態,故也有人說Python是一種多態語言。
組合
與繼承不同,組合不是什麼是什麼的關係,而是一種什麼有什麼的關係,比如學生有課程,他們是一種並行的關係而不是可以繼承的關係。
class Student: def __init__(self,ID,name,sex): self.id=ID self.name=name self.sex=sex self.course_list=[] class Course: def __init__(self,name,price,period): self.name=name self.price=price self.period=period s1=Student('123123123123','cobila','female') python_obj=Course('python',1,'7m') linux_obj=Course('linux',1,'2m') s1.course_list.append(python_obj) s1.course_list.append(linux_obj) print(s1.course_list[0].name) print(s1.course_list[0].price) 運行結果: python 1
關於組合的問題看到一個哥們在博客里的人狗大戰的代碼,與德瑪西亞大戰瑞文一樣,可以有一個裝備類,裝備有技能,也有屬性,可以通過購買裝備組合,也就是人與狗的實例化對象都可以擁有裝備,裝備加屬性加技能再進行交互。

class Person: # 定義一個人類 ''' 這是一個游戲裡人物的數據類型 ''' role = 'person' # 人的角色屬性都是人 def __init__(self, name, aggressivity, life_value): self.name = name # 每一個角色都有自己的昵稱; self.aggressivity = aggressivity # 每一個角色都有自己的攻擊力; self.life_value = life_value # 每一個角色都有自己的生命值; def attack(self,dog): # 人可以攻擊狗,這裡的狗也是一個對象。 dog.life_value -= self.aggressivity print("{0}打了{1}一下,{1}剩餘血量{2}".format(self.name, dog.name, dog.life_value)) class Dog: # 定義一個狗類 ''' 這是一個游戲里狗的數據類型 ''' role = 'dog' # 狗的角色屬性都是狗 def __init__(self, name, breed, aggressivity, life_value): self.name = name # 每一隻狗都有自己的昵稱; self.breed = breed # 每一隻狗都有自己的品種; self.aggressivity = aggressivity # 每一隻狗都有自己的攻擊力; self.life_value = life_value # 每一隻狗都有自己的生命值; def bite(self,people): # 狗可以咬人,這裡的狗也是一個對象。 people.life_value -= self.aggressivity print("{0}咬了{1}一下,{1}剩餘血量{2}".format(self.name,people.name,people.life_value)) class Weapon: ''' 這是一個游戲里武器的數據類型 ''' def __init__(self,name, price, aggrev, life_value): self.name = name #武器名稱 self.price = price #武器價格 self.aggrev = aggrev #武器傷害加成 self.life_value = life_value #武器血量加成 def update(self, obj): #obj就是要帶這個裝備的人 obj.money -= self.price # 用這個武器的人花錢買所以對應的錢要減少 obj.aggressivity += self.aggrev # 帶上這個裝備可以讓人增加攻擊 obj.life_value += self.life_value # 帶上這個裝備可以讓人增加生命值 def prick(self, obj): # 這是該裝備的主動技能,絞龍 obj.life_value -= 3000 # 假設攻擊力是3000 print("{0}發動主動技:蛟龍==>{1}剩餘血量{2}".format(self.name, obj.name, obj.life_value)) a = Person("蒼井井",10,1000) b = Dog("egon","狼狗",200,20000) c = Weapon("蛟龍鞭",1000,40,2000) a.money = 2000 #判斷是否買的起武器 if a.money > c.price : c.update(a) a.weapon = c #大戰開始 while True : a.attack(b) if b.life_value <= 0 : print(b.name + "被" + a.name + "打死了!") break a.weapon.prick(b) if b.life_value <= 0 : print(b.name + "被" + a.name + "絞死了!") break b.bite(a) if a.life_value <= 0 : print(a.name+"被"+b.name+"咬死了!") break人狗大戰實例
介面歸一化設計
父類中定義不操作,子類進行定義的衍生(子類都有相同功能,並且功能操作可能不一樣)。如果子類沒有定義或者也pass了,那這個方法就沒有實際的意義了,為了防止這種現象的出現要採取一些措施。

class Interface:#定義介面Interface類來模仿介面的概念,python中壓根就沒有interface關鍵字來定義一個介面。 def read(self): #定介面函數read pass def write(self): #定義介面函數write pass class Txt(Interface): #文本,具體實現read和write def read(self): print('文本數據的讀取方法') def write(self): print('文本數據的讀取方法') class Sata(Interface): #磁碟,具體實現read和write def read(self): print('硬碟數據的讀取方法') def write(self): print('硬碟數據的讀取方法') class Process(Interface): def read(self): print('進程數據的讀取方法') def write(self): print('進程數據的讀取方法') t1=Txt() s1=