Python基礎複習面向物件篇
目錄
- 類與物件的概念
- 例項方法
- 例項變數
- 初始化方法
- 析構方法
- 常用內建方法
- 繼承
- 類方法與靜態方法
- 動態擴充套件類與例項
- @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訪問屬性時,需要在屬性名前加上下劃線,否則系統會因不斷遞迴呼叫而報錯。
&n