目錄
- 類與對象的概念
- 實例方法
- 實例變量
- 初始化方法
- 析構方法
- 常用內置方法
- 繼承
- 類方法與靜態方法
- 動態擴展類與實例
- @property裝飾器
概述
面向對象是當前流行的程序設計方法,其以人類習慣的思維方法,用對象來理解和分析問題空間,使開發軟件的方法與過程盡可能接近人類認識世界丶解決問題的思維方法與過程。
面向對象方法的基本觀點是一切系統都是由對象構成的,每個對象都可以接收並處理其他對象發送的消息,它們的相互作用丶相互影響丶實現了整個系統的運轉
1.類與對象的概念
- 類和對象是面向對象程序設計的兩個重要概念
- 類和對象的關系即數據類型與變量的關系,根據一個類節約創建多個對象,而每個對象只能是某一個類的對象
- 類規定了可以用於存儲什么數據,而對象用於實際存儲數據,每個對象可存儲不同的數據
- 與C/C++等語言不同,Python規定萬物皆對象,即Python中提供的基本數據類型也是類,如int,float等。
類的定義
- 擁有相同屬性和行為的對象分為一組,即為一個類
- 類是用來描述對象的工具,用類可以創建此類的對象(實例)
- 屬性對象一個類可以用來保存哪些數據,而方法對應一個類可以支持哪些操作(即數據處理)
類的定義形式多樣
- 我們即可以直接創建新的類,也可以基於一個或多個已有的類創建新的類。
- 我們即可以創建一個空的類,然后再動態添加屬性和方法,也可以在創建類的同時設置屬性和方法。
- 每次創建對象時,系統都會在內存中選擇一塊區域分配給對象,每次選擇的內存通常是不一樣的。
類的創建語句 class 語句 語法: class 類名(繼承列表): '''類的文檔字符串''' 實例方法定義 類變量的定義 類方法(@classmethod) 定義 靜態方法(@staticmethod) 定義
說明:
繼承列表可以省略
類名必須是一個標識符(寫變量的命名相同,建議首字母大寫)
類名實質上就是變量,它綁定一個類,即指向這個類的地址
構造函數
構造函數調用表達式
類名([創建傳參列表])
作用:
創建這個類的實例對象,並返回此實例對象的引用關系
實例說明:
實例有自己的作用域和名字空間,可以為該實例添加實例變量(屬性)
實例可以調用實例方法和類方法
實例可以訪問實例變量和類變量
2.實例方法(instance method)
定義語法:
class 類名(繼承列表):
def 實例方法名(self, 參數1, 參數2, ...):
'''方法的文檔字符串'''
語句塊
作用:
用於描述一個對象的行為.讓此類型的全部對象都擁有相同的行為
說明:
實例方法的實質是函數,是定義在類內的函數
實例方法至少有一個形參,第一個形參用來調用(傳遞)這個方法的實例,一般命名為'self',self即這個實例本身
調用語法:
實例.實例方法名(調用傳參)
類名.實例方法名(實例, 調用傳參)
# 此示例示意實例方法的定義和調用 class Dog: '''這是一種小動物的定義''' def eat(self, food): '''此方法用來描述小狗吃的行為''' print('id為', id(self), "的小狗正在吃", food) def sleep(self, hour): print('id為', id(self), '的小狗睡了', hour, '小時') dog1 = Dog() # 創建一個對象 dog1.eat('骨頭') dog2 = Dog() # 創建另一個對象 dog2.eat('狗糧') dog1.sleep(1) dog2.sleep(3) Dog.eat(dog1, '包子') #等同於 dog1.eat("包子") 輸出 id為 60750064 的小狗正在吃 骨頭 id為 61271952 的小狗正在吃 狗糧 id為 60750064 的小狗睡了 1 小時 id為 61271952 的小狗睡了 3 小時 id為 60750064 的小狗正在吃 包子
3.實例變量(也稱為實例屬性)
每個實例可以有自己的變量,稱為實例變量(實例屬性)
語法:
實例.屬性名
作用:
記錄每個對象自身的數據
賦值規則:
首次為屬性賦值則創建此屬性,再次為屬性賦值則改變屬性的綁定關系
# 此示例示意實例變量的創建和訪問 class Dog: def eat(self, food): print(self.color, '的', self.kinds, '正在吃', food) self.last_food = food # 為正在吃的狗創建一個屬性 # 用來記住上次吃的是什么 def info(self): print(self.color, '的', self.kinds, '上次吃的是', self.last_food) dog1 = Dog() dog1.kinds = '導盲犬' # 為dog1綁定的對象添加kinds屬性 dog1.color = '灰色' # 初次賦值是創建變量color並綁定為'灰色' dog1.color = '黑色' # 再次賦值則改變color的綁定關系 # print(dog1.color, '的', dog1.kinds) dog1.eat("骨頭") dog2 = Dog() dog2.kinds = '哈士奇' dog2.color = '黑白色' dog2.eat('狗糧') dog1.info() dog2.info() 輸出: 黑色 的 導盲犬 正在吃 骨頭 黑白色 的 哈士奇 正在吃 狗糧 黑色 的 導盲犬 上次吃的是 骨頭 黑白色 的 哈士奇 上次吃的是 狗糧
刪除屬性
del 語句
語法:
del 對象.實例變量名
示例:
class Dog: pass dog1 = Dog() dog1.color = '白色' # <<<--- 添加實例變量 print(dog1.color) # 白色 del dog1.color # 刪除實例變量(屬性) print(dog1.color) # 屬性錯誤,沒有color這個屬性
4.初始化方法(構造方法):
作用:
對新創建的對象添加屬性
格式:
class 類名(繼承列表):
def __init__(self[, 形參列表]):
語句塊
說明:
1. 初始化方法名必須為__init__ 不可改變(注:雙下划線)
2. 初始化方法會在構造函數創建實例后自動調用,且將實例自身通過第一個參數self傳入__init__方法
3. 構造函數的實參將通過__init__方法的參數列表傳入到__init__ 方法中
4. 初始化方法內如果需要return 語句返回則必須返回None
5.析構方法:
作用:
析構方法是類的另一個內置方法,它的方法名為__del__, 在銷毀一個類對象時會自動執行.
負責完成待銷毀對象占用的資源清理工作,如關閉文件等
格式:
class 類名(繼承列表):
def __del_(self[):
語句塊
說明:
- 析構方法在對象(實例)銷毀前被自動調用
- python語言建議不要在對象銷毀時做任何事情,因為銷毀的時間難以確定
- 類對象銷毀有如下三種情況:
- 局部變量的作用域結束
- 使用del刪除對象
- 程序結束時,程序中的所有對象都將被銷毀
注意:
如果多個變量對應同一片內存空間,則只有這些變量都刪除后才會銷毀這片內存空間中保存的對象,也才會自動執行析構方法。
比如:
stu = student('小王') # 創建一個對象學生stu stu1 = stu del stu # 使用del刪除stu對象,但不會刪除stu變量指向的內存空間,即不會自動調用析構方法,因為stu1此時也對應着這一片內存空間
6.常用的內置方法:
__str__ :
調用str函數對類對象進行處理時或者調用Python內置函數format()以及print()時自動執行, __str__方法的返回值必須是字符串。
如果我們需要將一個對象當成字符串來使用的話,那么我們就必須在這個類中提供__str__ 方法。
有這個方法才能將類對象自動轉為使用時所需要的字符串。如果沒有則會輸出這個對象的地址。
例如:
class Complex: # 定義復數類Complex def __init__(self, real, image): self.real = real # 將self對應對象的real屬性賦值為形參real的值 self.image = image # 將self對應對象的image屬性賦值為形參image的值 def __str__(self): # 定義內置方法__str__ return str(self.real) + '+' + str(self.image) + 'i' c = Complex(3.2, 5.3) # 定義Complex類對象c print(c) # 輸出 3.2+5.3i
上例如果沒有定義__str__方法,則輸出<__main__.Complex object at 0x02C3EFD0>
比較運算的內置方法
內置方法 | 功能描述 |
__gt__(self,other) | 進行self>other運算時自動執行 |
__lt__(self,other) | 進行self<other運算時自動執行 |
__ge__(self,other) | 進行self>=other運算時自動執行 |
__le__(self,other) | 進行self<=other運算時自動執行 |
__eq__(self,other) | 進行self==other運算時自動執行 |
__ne__(self,other) | 進行self!=other運算時自動執行 |
大於 greater than 等於 equal 小於 less than 不等於 not equal
實例:
class Student: def __init__(self, name, age): self.name = name self.age = age def __le__(self, other): return self.age <= other.age stu = Student('李明', 19) stu1 = Student('馬紅', 20) print('馬紅的年齡小於等於李明的年齡:', stu1 <= stu) 輸出: 馬紅的年齡小於等於李明的年齡: False
7.繼承
繼承和派生的概念
- 繼承允許開發者基於已有的類創建新的類
-
派生類就是從一個已有類中衍生出新類,在新的類上可以添加新的屬性和行為
- 如果一個類C1通過繼承已有類C而創建,則將C1稱為2子類(sub class), 將C稱作基類、父類或超類(base class、super class).
- 子類會繼承父類中定義的所有屬性和方法, 另外也能夠在子類中增加新的屬性和方法。
- 如果一個子類只有一個父類,則將這種繼承關系稱為單繼承;如果一個子類有兩個或更多父類,則將這種繼承關系稱為多重繼承。
為什么繼承/派生
繼承的目的是延續舊類的功能
派生的目地是在舊類的基礎上添加新功能
作用:
用繼承派生機制, 可以將一些共有功能加在基類中,實現代碼共享
在不改變基類的代碼的基礎上改變原有類的功能
子類的定義:
class 子類名(父類1、父類2、...、父類M):
語句塊
當M 等於1時。則為單繼承;當M大於1時,則為多重繼承。
方法重寫
是指子類可以對從父類中繼承過來的方法進行重新定義,從而使得子類對象可以表現出對父類對象不同的行為。
如果在重定義的方法中,傳入不同的實例參數,系統會根據對象實際所屬的類去調用相應類中的方法。於是就出現了在執行同樣代碼的情況下,
輸出不同的結果,這種現象被稱為多態。
def Print_Info(對象實例): #普通函數 對象實例.PrintInfo() #調用該實例對象的方法
鴨子類型
在鴨子類型中,關注的不是對象所屬的類,而是一個對象能夠如何使用。
在Python中編寫一個函數,傳遞實參前其參數的類型並不確定,在函數中使用形參進行操作時只要傳入的對象能夠支持該操作程序就能正常執行。
所以,實際上,Python中的多態也是借助鴨子類型實現,與C++、Java等語言中的多態並不是同一含義。
super方法
super方法用於獲取父類的代理方法,以執行已在子類中被重寫的父類方法,其語法格式為:
super( [類名, [對象名或類名] ] )
super方法有兩個參數:
第一個參數是要獲取父類代理對象的類名。
第二個參數如果傳入對象名,則該對象所屬的類必須是第一個參數指定的類或該類的子類,找到的父類對象的self會綁定到這個對象上;
如果傳入類名, 則該類必須是第一個參數指定的類的子類。
在一個類A的定義中調用super方法時,可以將兩個參數都省略, 此時,super( )等價於super(A, self), 即獲取A的父類代理對象,且獲取
到的父類代理對象中的self綁定到當前A類對象的self上。
有關類的內置函數
isinstance: 用於判斷一個對象所屬的類是否是指定類或指定類的子類;
issubclass: 用於判斷一個類是否是另一個類的子類;
type: 用於獲取一個對象所屬的類。
8.類方法和靜態方法
類方法:
類方法是指使用@classmethod修飾的方法, 其第一個參數是類本身(而不是類的實例對象)。
類方法的特點是既可以通過類名直接調用,也可以通過類的實例對象調用。
1 class Complex: # 定義復數類Complex 2 def __init__(self, real=0, image=0): 3 self.real = real # 將self對應對象的real屬性賦值為形參real的值 4 self.image = image # 將self對應對象的image屬性賦值為形參image的值 5 6 @classmethod 7 def add(cls, c1, c2): # 定義類方法add, 完成兩個數相加 8 print('cls的值為:', cls) 9 c = Complex() #創建Complex對象c 10 c.real = c1.real + c2.real # 實部相加 11 c.image = c1.image + c2.image # 虛部相加 12 return c 13 14 c1 = Complex(1, 2.5) 15 c2 = Complex(2.2, 3.1) 16 c = Complex.add(c1, c2) # 直接使用類名調用類方法add 17 print('c1+c2=%.2f+%.2fi' %(c.real,c.image)) 18 19 輸出: 20 cls的值為: <class '__main__.Complex'> 21 c1+c2=:3.20+5.60i
提示:
將16行的 "c = Complex.add(c1, c2)" 改成 “c = c1.add(c1, c2)” 或 "c = c2.add(c1, c2)" 或 "c = Complex().add(c1, c2)", 將程序運行后可得到相同的輸出結果,
即類方法也可以使用實例對象調用。
通過類方法add的第一個參數,從輸出結果中可以看到 cls 是 Complex類。
靜態方法
靜態方法是指使用@staticmethod修飾的方法。
與類方法相同,靜態方法既可以直接通過類名調用,也可以通過類的實例對象調用。
與類方法不同的地方在於,靜態方法中沒有類方法中的第一個類參數。
9.動態擴展類與實例
- python作為一種動態語言,除了可以在定義類時定義屬性和方法外,還可以動態地為已經創建的對象綁定新的屬性和方法
- 在給對象綁定方法時,需要使用types模塊中的MethodType方法,其第一個參數是要綁定的函數名,第二個參數是綁定的對象名。
- 動態綁定屬性時,可直接在對象上綁定,但要注意__slots__屬性的限制。
例:綁定新方法實例
from types import MethodType class Student: pass def SetName(self, name): # 定義SetName函數 self.name = name def SetSno(self, son): # 定義SetSno函數 self.son = son stu1 = Student() stu2 = Student() stu1.SetName = MethodType(SetName, stu1) # 為stu對象綁定SetName方法 Student.SetSno = SetSno # 為Student類綁定Setno方法 stu1.SetName('李曉明') stu1.SetSno('1810100') # stu2.SetName('張剛') # 取消注釋則會報錯 stu2.SetSno('1810101')
注意: 如果只是給一個對象綁定一個新方法,那么只有這個對象有這個方法,只能通過該對象調用該方法。同一個類的其他對象沒有這個方法。
如果是給一個類綁定新方法,那么這個類的所有實例都有這個新方法。
__slots__
- 在定義類時,python提供了__slots__變量以限制可動態擴展的 屬性。
- __slots__中所做的動態擴展屬性限制只對__slots__所在類的實例對象有效
- 如果子類中沒有__slots__定義,則子類的實例對象可以進行任意屬性的動態擴展。
- 如果子類中有__slots__定義,則子類的實例對象可動態擴展的屬性包括子類中通過__slots__定義的屬性和其父類中通過__slots__定義的屬性。
例: __slots__使用實例
class Person: __slots__ = ('name') # 定義允許動態擴展的屬性 class Student(Person): # 以Person類作為父類定義子類Student類 __slots__ = ('sno') # 定義允許動態擴展的屬性 class Postgraduate(Student): # 以Student類作為父類定義子類Postgraduate類 pass stu = Student() stu.sno = '1810100' # 為stu對象動態擴展屬性sno stu.name = '李曉明' # 為stu對象動態擴展屬性name #stu.tutor = '馬紅' # AttributeError: 'Student' object has no attribute 'tutor' pg = Postgraduate() # 定義Postgraduate類對象 pg.sno = '1810101' # 為pg動態擴展屬性son pg.name = '張剛' # 為pg對象動態擴展屬性name pg.tutor = '馬紅' # 為pg對象動態擴展屬性tutor
10.@property裝飾器
類中的屬性可以直接訪問和賦值,這為類的使用者提供了方便,但也帶來了問題: 類的使用者可能可能會給一個屬性賦上超出有效范圍的值。
為了解決這個問題,Python提供了@property裝飾器, 可以將類中屬性的訪問和賦值操作自動轉為方法調用,這樣可以在方法中對屬性值得取值范圍做一些條件限定
直接使用@property就可以定義一個用於獲取屬性值的方法(即getter)
如果要定義一個設置屬性值的方法(setter),則需要使用名字 "@屬性值.setter"的裝飾器。
如果一個屬性只有用於獲取屬性值的getter方法,而沒有用於設置屬性值的setter方法,則該屬性是一個只讀屬性,只允許讀取該屬性的值,而不能設置該屬性的值。
例:通過@property裝飾器使得學生成績的取值范圍必須在0~100之間。
import datetime class Student: # 定義Student類 @property def score(self): # 用@property裝飾器定義一個用於獲取score值的方法 return self._score @score.setter def score(self, score): # 用score.setter定義一個用於設在score值的方法 if score<0 or score>100: # 不符合0~100的限定條件 print('成績必須在0~100之間') else: self._score = score @property def age(self): # 用@property裝飾器定義一個用於獲取age值的方法 return datetime.datetime.now().year-self.birthyear stu = Student() # 創建Student類對象Stu stu.score = 80 # 將stu對象的score屬性賦值為80 stu.birthyear = 2000 # 將stu對象的birthyear屬性賦值為2000 print('年齡:%d,成績:%d' %(stu.age, stu.score)) # stu.age = 19 # 取消注釋會報錯, 因為在student類的age屬性只設置了getter方法, # 而沒有設置setter方法(沒有@age.setter),所以age只能取值而不能賦值,即age是只讀屬性 stu.score = 105 # print('年齡:%d,成績:%d' %(stu.age, stu.score))
輸出:
年齡:19,成績:80 成績必須在0~100之間 年齡:19,成績:80
注意:在類的setter'和getter方法中使用self訪問屬性時,需要在屬性名前加上下划線,否則系統會因不斷遞歸調用而報錯。