面向對象
編程方式
- 面向過程
- 函數式編程
- 面向對象
面向過程
-
之前所用的,按照解決問題的步驟,一步步的按流程進行,有先後之分。整個設計流水線式的,思維比較機械化。
-
優缺點:
- 優點 : 複雜的問題流程化,將問題分解簡化。
- 缺點:拓展性不足
面向對象
定義
核心是對象.對象是一個數據以及其相關行爲的集合,
面對對象是功能上指向建模對象,通過數據和行爲方式來描述交互對象的集合.
在python中,一切皆爲對象.
優缺點
- 優點
可以解決程序的拓展性
- 缺點
就是複雜度遠高於面向過程.
交互式解決問題無法準確預測結果
內容
- 類(Class): 用來描述具有相同的屬性和方法的對象的集合。它定義了該集合中每個對象所共有的屬性和方法。對象是類的實例。
- **方法:**類中定義的函數。
- **類變量:**類變量在整個實例化的對象中是公用的。類變量定義在類中且在函數體之外。類變量通常不作爲實例變量使用。
- **數據成員:**類變量或者實例變量用於處理類及其實例對象的相關的數據。
- **方法重寫:**如果從父類繼承的方法不能滿足子類的需求,可以對其進行改寫,這個過程叫方法的覆蓋(override),也稱爲方法的重寫。
- **局部變量:**定義在方法中的變量,只作用於當前實例的類。
- **實例變量:**在類的聲明中,屬性是用變量來表示的。這種變量就稱爲實例變量,是在類聲明的內部但是在類的其他成員方法之外聲明的。
- **繼承:**即一個派生類(derived class)繼承基類(base class)的字段和方法。繼承也允許把一個派生類的對象作爲一個基類對象對待。例如,有這樣一個設計:一個Dog類型的對象派生自Animal類,這是模擬"是一個(is-a)"關係(例圖,Dog是一個Animal)。
- **實例化:**創建一個類的實例,類的具體對象。
- **對象:**通過類定義的數據結構實例。對象包括兩個數據成員(類變量和實例變量)和方法。
舉例
object1:Tom
特徵:
scool=zucc
name=tom
age=21
sex=male
技能:ballgame study ...
object2:Bob
scool=zucc
name=bob
age=22
sex=male
技能:tennis study ...
類就是類別,種類.
對象就是特徵和技能的統一體.
類就是這一系列相似對象的特徵和技能的結合.
對應現實生活,先有個體(對象),纔有類別;但是對於程序,必須先有類,然後纔有對象.
面向對象編程引入
定義
OOP
(object oriented programming)即面向對象編程
是一種程序設計思想.OOP
把對象作爲程序的一個基本單元,一個對象就包含了數據和操作數據的函數.
在python中,所有數據類型都可以視爲對象,當然也可以自定義對象.
自定義對象的數據類型就是面向對象中類的概念
舉例
如要處理我們的成績.爲了表示學生的成績:
- 面向過程的方式
st1={'name':'jack','score':80}
st2={'name':'bob','score':97}...
def show_score(st):
print(st['name'],st['score'])
show(st1)
- 面向對象
class student:
def __init__(self,name,score):
self.name=name
self.score=score
def show_score(self):
print(self.name,':',self.score)
st1=student('bob',97)
st1.show_score()
類的定義
面向對象設計的思想,先抽象出類,再根據類創建實例.
命名類名時 變量名稱各個單詞首字母大寫
創建一個空類
class MyFirstClass:
pass
創建一個普通類
class ClassName(object):
'''dicstring(說明文檔)'''
class_statement
class Student:
school='ZUCC'#所有對象的固有屬性
def __init__(self,name,score):
self.name=name
self.score=score
def show_score(self):
print(self.name,':',self.score)
st1=Student('bob',97)#實例化
print((st1.name,st1.score,st1.school))
('bob', 97, 'ZUCC')
總結
類的作用是一個模板.我們可以在創建實例的時候,把一些我們認爲必須要綁定的屬性填寫進去.這時就通過特殊的__init__
方法.在創建實例的時候,綁定相關的屬性.
和普通函數相比,在類中定義方法時,第一個參數必須是self.除第一個參數外,其他的與普通函數沒有什麼區別.
self代表的是實例,而非類
__init__
方法
- 爲對象初始化自己獨有的特徵
- 該方法中可以有任意的代碼,但是不能有返回值
數據封裝
class Student:
school='ZUCC'#所有對象的固有屬性
def __init__(self,name,score):
self.name=name
self.score=score
st1=Student('bob',97)#實例化
我們通過__init__
讓st1
實例本身有了相關的數據,我們可以直接在Student類的內部定義相關的函數來訪問數據,以此封裝數據.
這些封裝數據的函數和Student類本身是關聯起來的,他們被稱之爲方法.
類的兩個作用
-
類名引用
類名.屬性
-
實例化
類名加上一個括號就是實例化,它能夠自動觸發__init__
函數,進而爲每個實例定製自己的特徵
- 類屬性的補充
類屬性的查看
-
dir
()來查看對應類包含的屬性(返回列表) -
類名.
__dict__
返回一個字典,key爲屬性名,value爲屬性值
特殊的類屬性
類名.__name__
返回類的名字
類名.__doc__
返回類的文檔字符串
類名.__base__
返回類的第一個父類
類名.__bases__
返回類的所有父類(元組)
類名.__module__
返回類定義所在的模塊
類名.__class__
返回實例對應的類
對象之間的交互
定義2個類,Person,Dog,都有體力及攻擊值,可以進行攻擊
class Person:
def __init__(self,name,aggressivity,life_value):
self.name=name
self.aggre=aggressivity
self.life_values=life_value
def attack(self,dog):
dog.life_values-=self.aggre
class Dog:
def __init__(self,name,breed,aggressivity,life_vale):
self.name=name
self.breed=breed
self.aggre=aggressivity
self.life_values=life_vale
def bite(self,people):
people.life_values-=self.aggre
per=Person('bob',10,888)
dog=Dog('white','husky',12,480)
print('dog:',dog.life_values,',person:',per.life_values,sep='')
per.attack(dog)
dog.bite(per)
print('dog:',dog.life_values,',person:',per.life_values,sep='')
dog:480,person:888
dog:470,person:876
類命名空間與對象實例的空間
創建一個類就會創建一個類的名稱空間,用來存儲我們定義的所有的變量名。這些名字就是屬性。
類的屬性有兩種
-
靜態 :直接在類中定義的變量
-
動態 : 在類中定義的方法
靜態屬性是共享給所有對象的,動態屬性是綁定到所有對象的
class Student:
school='ZUCC'#所有對象的固有屬性(靜態)
def __init__(self,name,score):
self.name=name
self.score=score
def find_score(self): ##(動態)
print(self.name,":",self.score)
st1=Student('bob',95)
st2=Student('jack',89)
print(id(st1.school),id(st2.school))
print(id(st1.find_score),id(st2.find_score),id(Student.find_score
4365888 4365888
4014768 4014768 12138368
類的三大特性
繼承
在面向對象編程中,當我們定義一個新的類後,可以從某個現有的類,繼承,新的類就被稱爲子類
SubClass
,而被繼承的類則被稱爲基類,父類,超累(Base Class,Father Class,Super Class
)
#定義一個動物類(Animal),它有一個run()方法
class Animal: #定義父類
def run(self):
print('Animal is running.')
class Dog(Animal): #括號內爲所繼承的類
pass
class Cat(Animal): #單繼承
pass
class Husky(Animal,Dog): #多繼承用逗號分隔開
pass
Dog().run() #繼承Animal的run
Cat().run()
print(Dog.__bases__) #查看該類繼承的父類
print(Animal.__bases__)
Animal is running.
Animal is running.
(<class '__main__.Animal'>,)
(<class 'object'>,)
如果不指定類,python類會默認繼承object類
object是所有python類的基類,提供一些常見方法的實現
多態
當子類和父類存在相同名稱的方法時,子類的方法會覆蓋父類的方法,在運行代碼時,總會調用子類的方法。
這樣就是繼承的另外一個好處,多態。
理解多態,首先要對數據類型再進行說明。定義一個類的時候實際上就是定義了一種數據類型。我們自定義的數據類型和python自帶的數據類型(str,list,dict)等相同。
class Animal:
def run(self):
print('Animal is running.')
class Dog(Animal):
def run(self):
print('Dog is Running')
class Cat(Animal):
def run(self):
print('Cat is Running')
dog=Dog()
cat=Cat()
dog.run()
cat.run()
Dog is Running
Cat is Running
###isinstance判斷所給的對象是不是所給的數據類型
li=[]
print(isinstance(li,list))
print(isinstance(dog,Animal))
print(isinstance(Dog,object))
True
True
True
對於一個變量,只要知道其父類型,無需確切知道其子類型,就可以調用相關的方法。運行時具體的方法是作用於子類型上還是作用於父類型上,由我們運行的對象決定。也就是說,在調用時只管調用,無需注意細節
當我們新增一個子類時,只要保證相關的方法編寫正確,就不用再管原來的代碼是怎麼調用的
開閉原則
- 對拓展開放:允許新增子類
- 對修改封閉:不需要修改依賴父類類型的函數
總結
繼承可以一級一級的繼承下來
任何類都可以追溯到根類object
私有屬性
在類的內部,可以有屬性和方法,而外部代碼就可以通過直接調用實例變量的方法來操作數據.這樣,隱藏了內部的複雜邏輯
class Student:
school='ZUCC'#所有對象的固有屬性(靜態)
def __init__(self,name,score):
self.name=name
self.score=score
def find_score(self): ##(動態)
print(self.name,":",self.score)
st1=Student('bob',95)
st1.find_score()
st1.score=92
st1.find_score()
bob : 95
bob : 92
由上可知,外部代碼可以自由修改一個實例的屬性(name,score).
如果要讓內部屬性不被外部訪問,我們可以在屬性名稱前面加__(2個下劃線.)
在python中如果實例的變量名以雙下劃線開頭,就變成了一個私有變量,只允許內部訪問.
class Student:
school='ZUCC'#所有對象的固有屬性(靜態)
def __init__(self,name,score):
self.name=name
self.__score=score
def find_score(self): ##(動態)
print(self.name,":",self.__score)
st1=Student('bob',95)
st1.find_score()
print(st1.name)
print(st1.__score)
bob : 95 #成績只能通過內部函數訪問
bob #可以直接外部訪問
AttributeError: 'Student' object has no attribute '__score' #報錯,無法直接無法及更改分數
如果想要更改私有變量,可以通過在類內定義更改該變量的函數,再在外部調用函數
class Student:
school='ZUCC'#所有對象的固有屬性(靜態)
def __init__(self,name,score):
self.name=name
self.__score=score
def find_score(self): ##(動態)
print(self.name,":",self.__score)
def change_score(self,score):
self.__score=score
st1=Student('bob',17)
st1.find_score()
st1.change_score(89)
st1.find_score()
bob : 17
bob : 89
########注:並非絕對無法從外部訪問私有變量
print(st1._Student__score)
89
#可以以以上方式訪問私有屬性,但是不推薦這種方式.
封裝
定義
隱藏對象的屬性和實現細節,僅對外提供公共訪問的方式
優點
- 可以將變化隔離
- 便於使用
- 提高安全性
- 提高複用性
原則
-
將不需要對外提供的內容隱藏
-
隱藏屬性,提供公共方法對其進行訪問
————>私有方法,私有變量------->私有屬性
用雙下劃線開頭標記私有屬性
鴨子類型
解釋
關於鴨子類型,在百科中是這麼描述的:“鴨子類型”的語言是這麼推斷的:一隻鳥走起來像鴨子、遊起泳來像鴨子、叫起來也像鴨子,那它就可以被當做鴨子。也就是說,它不關注對象的類型,而是關注對象具有的行爲(方法)。對於C++來說,我們要調用某個以類A爲參數的函數,則我們傳入的類型必須是類A類型,或者是類A的子類類型。但在Python中,卻沒有這個要求,它只要求我們在函數體中實現部分,在類B中同樣也能實現,那麼B就能傳入函數中。
class obj1:
def run(self):
print('this is first')
class obj2:
def run(self):
print('this is second')
def runs(obj1):
obj1.run()
StuA=obj1()
runs(StuA)
infA=obj2()
runs(infA)
this is first
this is second