今日任務:
- 類的定義
- pass佔位語句
- 類的初始化方法
- 類的普通方法
- 類的繼承:屬性的繼承、方法的繼承
類的定義
對類進行定義時,使用class關鍵字,注意與函數不同,在類名的後面不需要加括號,示例如下:
class ClassName: #類名通常遵循大駝峯命名法 (UpperCamelCase),即每個單詞的首字母都大寫
"""類的文檔字符串"""
# 類的內容
pass #pass 為佔位符,避免運行錯誤
關於pass與縮進:
- Python通過縮進定義代碼塊的結構,對於def,class,if等,如果後續代碼塊為空,那麼就會因為無法確定結構的範圍而拋出IndentationError。
- pass本身不執行任何操作,但它可以起到一個佔位置的作用。因此,當需要一個語法上存在的代碼塊,但又暫時不想在其中放入任何實際的邏輯時,就可以使用pass佔位。
在定義一個類時,它可以具有屬性和方法兩個部分。
- 屬性(是什麼):用於存儲數據,初始化方法構造、類內部直接定義、類外部添加
- 方法(做什麼):定義行為,初始化方法(__init__)、類方法、靜態方法、魔術方法等等
類的方法
上面提到了類的方法和屬性,在調用時,對方法和屬性分別使用example.method()、example.name。
下面主要説明初始化方法和自定義的普通方法。
初始化方法
__init__(注意前後均各有兩條下劃線)屬於類的構造方法,當創建對象時,會被自動調用。
關於參數,可以自己使用self.屬性名給出默認值或者賦值給外部傳入的參數值。
class Teacher:
def __init__(self, name, age):# 初始化方法,傳入了參數
print(f"self的內存地址: {id(self)}") #打印內存地址
self.name = name # 外界的參數,需要通過self.xxx來複制給類自己的屬性
self.age = age
self.subject = "English" # 這個屬性仍然是在創建時就設定好的
# 創建一個Teacher對象的例子,構造方法的參數必須
teacher_1 = Teacher("Susan", 33) # 如果在初始化方法中設置了非默認的參數,那麼外界就必須要傳入才行
print(f"teacher_1的內存地址: {id(teacher_1)}")
print(teacher.name) # 輸出: Susan
print(teacher.age) # 輸出: 33
print(teacher.subject) # 輸出: English
teacher_1.subject = 'Math' #修改屬性
print(teacher.subject) # 輸出: Math
在使用__init__初始化時,必須先填入'self',因為self代表的是實例本身(打印內存地址查看):
- 對同一個類,可以創建不同的實例,每次調用,可以將不同實例作為self參數傳入
- 通過self.屬性名和self.方法名()獲取實例的屬性和方法
- 數據隔離:確保每個實例有獨立的數據空間
注意在對類實例化時,傳入的參數是初始化方法中的(一些屬性)。後續在普通方法中的參數,是在調用函數的時候傳入(與之前的普通函數調用相同)。當然,這些參數肯定是除‘self’之外的。
普通方法(自定義)
與Init方法不同,自定義的方法只有手動調用時才會被執行。但相同的時,第一個參數也是'self',原因同上。其他的寫法與之前單獨的函數寫法基本相同,但區別如下:
- 第一個參數是“self”,後面是正常的參數
- 需要在函數內部使用類的屬性和方法時,必須使用self,比如self.name而不是name
class Teacher:
# 初始化方法接受參數以動態設置教師的屬性
def __init__(self, name, subject, age):
self.name = name
self.subject = subject
self.age = age
# 不是init的都叫做普通方法
# 普通方法,模擬教師上課的行為
def teach_lesson(self):
print(f"{self.name}正在教{self.subject}。")
# 另一個普通方法,模擬教師批評學生的行為
def criticize(self, student_name):
print(f"{self.name}正在批評{student_name}。")
# 創建Teacher類的實例
teacher = Teacher("Susan", "English", 33)
# 調用教師的方法
teacher.teach_lesson()
teacher.criticize("John")#普通方法的參可以等到調用該方法的時候再傳
關於構造方法與普通方法的對比:
類的繼承
類的繼承是面向對象編程的重要特性,允許基於已有的類創建新類,而新類會自動獲得父類的屬性和方法,並可以添加新的功能或修改現有功能。
使用類的繼承,實際上很大程度上與之前的函數封裝、裝飾器類似,就是減少代碼重複。比如定義了一個貓類別和狗類別,然後發現它們中存在相同的地方(吃東西、睡覺),完全可以將這些提取出來,使用一個父類(動物)來包含。貓類和狗類只要繼承就可以,而對應於別的動物(魚類、鳥類)也可以適用。
此外,適用類的繼承有如下用途:
- 建立層次關係,更精確地建模現實世界,比如上面的動物——貓/狗/魚/鳥
- 擴展和定製功能,在每一個子類中可以重寫或新增屬性或方法作為自身的特色
- 多態性、便於維護和更新(修改父類可影響所有子類)
在用代碼實現時需要注意:
- 類名後的括號中填入父類名稱(可以不只一個)
- 使用super()調用父類方法:子類初始化或者需要擴展父類方法時使用;完全重寫或新增方法時不需要
下面是代碼示例:
# 定義一個父類
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
def sleep(self):
print(f'{self.name}在睡覺')
def speak(self):
print(f"{self.name} 發出聲音")
class Dog(Animal): #繼承需要在括號中指定父類
def __init__(self,name,age,color): #傳入父類的參數,同時也可以傳入自己的參數
# super()函數 除了在構造方法中使用,還可以在其他方法中使用
super().__init__(name,age) #調用父類的構造方法,super()是一個內置函數,返回父類的實例
self.color = color #新增子類特有的屬性
#重寫方法,如果子類定義了與父類同名的方法,子類實例會優先調用子類的方法。
def sleep(self):
print(f'{self.color}的{self.name}在睡覺')
#重寫——擴展父類功能
def speak(self):
super().speak() # 先調用父類的方法
print("汪汪叫") # 再添加子類的行為
#新增功能
def run(self):
pass
dog = Dog("旺財", 3,'紅色')
dog.sleep()
作業
題目1:定義圓(Circle)類
要求:
- 包含屬性:半徑 radius。
- 包含方法:calculate_area():計算圓的面積(公式:πr²)。calculate_circumference():計算圓的周長(公式:2πr)。
- 初始化時需傳入半徑,默認值為 1。
import math
class Circle:
def __init__(self,radius):
self.radius = radius
def calculate_area(self): #計算面積
area = round(math.pi*pow(self.radius,2),2)
return area
def calculate_circumference(self): #計算周長
circumference = round(2*math.pi*self.radius,2)
return circumference
#調用
circle = Circle(5)
print(f"半徑:{circle.radius}") # 輸出:半徑:5
print(f"面積:{circle.calculate_area()}") # 輸出:面積:78.54
print(f"周長:{circle.calculate_circumference()}") # 輸出:周長:31.42
題目2:定義長方形(Rectangle)類
- 包含屬性:長 length、寬 width。
- 包含方法:calculate_area():計算面積(公式:長×寬)。calculate_perimeter():計算周長(公式:2×(長+寬))。 is_square() 方法,判斷是否為正方形(長 == 寬)。
- 初始化時需傳入長和寬,默認值均為 1。
class Rectangle:
def __init__(self,length=1,width=1): #長和寬默認為1
self.length = length
self.width = width
def calculate_area(self): #計算面積
area = self.length * self.width
return area
def calculate_perimeter(self): #計算周長
perimeter = 2*(self.length + self.width)
return perimeter
def is_square(self): #判斷是否為正方形
return self.length == self.width
rect = Rectangle(4, 6)
print(f"長:{rect.length}, 寬:{rect.width}") # 輸出:長:4, 寬:6
print(f"面積:{rect.calculate_area()}") # 輸出:面積:24
print(f"周長:{rect.calculate_perimeter()}") # 輸出:周長:20
print(f"是否為正方形:{rect.is_square()}") # 輸出:是否為正方形:False
square = Rectangle(5, 5)
print(f"是否為正方形:{square.is_square()}") # 輸出:是否為正方形:True
題目3:圖形工廠
創建一個工廠函數 create_shape(shape_type, *args),根據類型創建不同圖形對象:圖形工廠(函數或類)。
示例:shape_type='circle':創建圓(參數:半徑);shape_type='rectangle':創建長方形(參數:長、寬)
import math
# 定義圓類
class Circle:
def __init__(self,radius):
self.radius = radius
def calculate_area(self): #計算面積
area = round(math.pi*pow(self.radius,2),2)
return area
def calculate_circumference(self): #計算周長
circumference = round(2*math.pi*self.radius,2)
return circumference
# 定義長方形類
class Rectangle:
def __init__(self,length=1,width=1): #長和寬默認為1
self.length = length
self.width = width
def calculate_area(self): #計算面積
area = self.length * self.width
return area
def calculate_perimeter(self): #計算周長
perimeter = 2*(self.length + self.width)
return perimeter
def is_square(self): #判斷是否為正方形
return self.length == self.width
# 圖形工廠
def create_shape(shape_type,*args):
if shape_type.lower() == 'circle':
return Circle(args[0])
elif shape_type.lower() == 'rectangle':
return Rectangle(args[0],args[1])
else:
return '抱歉,目前支持創建圓或長方形!'
# 測試
shape1 = create_shape("Circle", 5)
print(shape1.calculate_circumference()) # 輸出:31.42
shape2 = create_shape("RecTangle", 3, 4)
print(shape2.is_square()) # 輸出:False
shape3 = create_shape('triangle',3,4,5)
print(shape3) # 抱歉,目前支持創建圓或長方形!