前言:本篇講解面向對象的三大特征(封裝,繼承,多態),還有比較細致的(類屬性類方法,靜態方法),分步驟講解,比較適合理清楚三大特征的思路
面向對象的的三大特征:
封裝------根據職責將屬性和方法封裝到一個抽象的類中 ; 增強代碼的安全性
繼承------實現代碼的重用,相同的代碼不需要重復的編寫 ; 增強代碼的可重用性
多態------不同的對象調用相同的方法,產生不同的執行結果,增加代碼的靈活度 。 增強代碼的可擴展性
封裝
1.1 概念:
封裝是將數據(屬性)和操作數據的方法(行為)捆綁在一起,形成一個獨立的單元,即對象。同時,對外部隱藏對象的內部實現細節,只提供公開的接口供外部訪問和操作,以此增強數據的安全性和程序的可維護性。
把現實世界中的主體中的屬性和方法書寫到類的里面的操作即為封裝封裝可以為屬性和方法添加為私有權限,不能直接被外部訪問
1.2 封裝中的私有屬性和私有方法:
在面向對象代碼中,我們可以把屬性和方法分為兩大類:公有(屬性、方法)、私有(屬性、方法)Python:公有(屬性、方法),私有(屬性、方法)Java:公有(屬性、方法),受保護(屬性、方法),私有(屬性、方法)公有屬性和公有方法:無論在類的內部還是在類的外部我們都可以對屬性和方法進行操作。但是有些情況下,我們不希望在類的外部對類內部的屬性和方法進行操作。我們就可以把這個屬性或方法封裝成私有形式。
1.3 定義方式:
在 Python 里,通過命名約定來實現訪問控制。以單下劃線
_
開頭的屬性和方法被視為受保護的,雖外部仍可訪問,但開發者通常將其作為內部使用的標識;以雙下劃線__
開頭的屬性和方法被視為私有的,外部不能直接訪問。
class BankAccount:def __init__(self, balance):# 私有屬性self.__balance = balancedef deposit(self, amount):self.__balance += amountreturn self.__balancedef withdraw(self, amount):if amount <= self.__balance:self.__balance -= amountreturn self.__balanceelse:return "余額不足"def get_balance(self):return self.__balanceaccount = BankAccount(1000)
# 不能直接訪問私有屬性
# print(account.__balance) 會報錯
print(account.get_balance())
當然私有屬性不能在類的外部被直接訪問,我們也有方法。
1.4 私有屬性設置與訪問"接口"
讀取私有屬性的接口(Getter 方法)
為了讓外部代碼能夠安全地獲取私有屬性的值,可以在類中定義一個公共的方法,通常稱為 Getter 方法。
class Person:def __init__(self, name, age):self.__name = nameself.__age = age# Getter 方法,用于獲取私有屬性 __name 的值def get_name(self):return self.__name# Getter 方法,用于獲取私有屬性 __age 的值def get_age(self):return self.__agep = Person("Alice", 25)
print(p.get_name()) # 輸出: Alice
print(p.get_age()) # 輸出: 25
修改私有屬性的接口(Setter 方法)
如果需要允許外部代碼修改私有屬性的值,可以在類中定義一個公共的方法,通常稱為 Setter 方法。在 Setter 方法中,可以添加一些驗證邏輯,確保修改的值是合法的。
class Person:def __init__(self, name, age):self.__name = nameself.__age = age@propertydef name(self):return self.__name@name.setterdef name(self, new_name):if isinstance(new_name, str):self.__name = new_nameelse:print("名稱必須是字符串類型")@propertydef age(self):return self.__age@age.setterdef age(self, new_age):if isinstance(new_age, int) and new_age >= 0:self.__age = new_ageelse:print("年齡必須是一個非負整數")p = Person("Alice", 25)
print(p.name) # 輸出: Alice
p.name = "Bob"
print(p.name) # 輸出: Bob
總結:在Python中,一般定義函數名' get_xx '用來獲取私有屬性,定義' set_xx '用來修改私有屬性值。
繼承
2.1 概念:
繼承是指一個類(子類、派生類)可以繼承另一個類(父類、基類)的屬性和方法。子類能夠復用父類的代碼,還可添加新屬性和方法,或者修改父類的方法以實現不同行為。
2.2 作用:
代碼復用:避免重復編寫相同代碼,提高開發效率。例如多個類有共同屬性和方法,可將這些提取到父類,子類直接繼承使用。
功能擴展:子類在繼承父類基礎上,能添加新屬性和方法,滿足不同需求。
建立類的層次結構:有助于組織和管理代碼,體現類之間的關系。
2.3 基本語法:
class B(object):pass
class A(B):pass
a = A()
a.B中的所有公共屬性
a.B中的所有公共?法
2.4 單繼承:
一個子類只繼承一個父類。不能同時繼承多個類。這個類會有具有父類的屬性和方法。這是最常見的繼承方式,結構清晰,易于理解和維護。
基本語法:
# 1、定義?個共性類(?類)
class Car(object):def run(self):print('i can run')
# 2、定義汽油?
class GasolineCar(Car):pass
# 3、定義電動?
class EletricCar(Car):pass
bwm = GasolineCar()
bwm.run()
單繼承的傳遞性:
單繼承的傳遞性可以描述為:如果類
C
繼承自類B
,而類B
又繼承自類A
,那么類C
不僅會繼承類B
中定義的屬性和方法,還會繼承類A
中定義的屬性和方法。這種傳遞性可以一直延續下去,無論繼承層次有多深,子類都能獲取到其所有祖先類的非私有屬性和方法。
class C(object):def func(self):print('我是C類中的相關?法func')
class B(C):def funb(self):print('我是B類中的相關?法funb')
class A(B):def funA(self):print('我是A類中的相關?法funa')a = A()
a.funa()
b = B()
b.funb()
c = C()
c.func()
2.5 object:
在 Python 中,
object
是所有類的基類,在 Python 面向對象編程體系里扮演著非常基礎且重要的角色,所有類默認都會隱式繼承自object
類。也就是說,即使你定義類時沒有顯式指定基類,它實際上也是object
類的子類。
# 定義一個沒有顯式指定基類的類
class MyClass:pass# 檢查 MyClass 是否是 object 的子類
print(issubclass(MyClass, object)) # 輸出: True
2.6 多繼承:
多繼承是指一個子類可以同時繼承多個父類的特性。也就是說,子類可以擁有多個父類所定義的屬性和方法,從而可以復用多個不同父類的代碼。
# 定義第一個父類
class Parent1:def method1(self):print("This is method1 from Parent1")# 定義第二個父類
class Parent2:def method2(self):print("This is method2 from Parent2")# 定義子類,繼承自 Parent1 和 Parent2
class Child(Parent1, Parent2):pass# 創建子類的實例
child = Child()# 調用從 Parent1 繼承的方法
child.method1()# 調用從 Parent2 繼承的方法
child.method2()
2.7 重寫:
子類重寫父類中的屬性與方法,子類可以重寫父類的方法,即子類定義一個與父類同名的方法,這樣在調用該方法時,會優先使用子類的方法。
基本語法:
class Father(object):屬性?法
class Son(Father):?類屬性和?法??的屬性和?法(如果?類中的屬性和?法與?類中的屬性或?法同名,則?類中的屬性或?法會對?類中同名的屬性或?法進?覆蓋(重寫))
示例代碼:
class Animal(object):def eat(self):print('i can eat')# 公共?法def call(self):print('i can call')
class Dog(Animal):# 重寫?類的call?法def call(self):print('i can 汪汪汪')
class Cat(Animal):# 重寫?類的call?法def call(self):print('i can 喵喵喵')
wangcai = Dog()
wangcai.eat()
wangcai.call()
miaomiao = Cat()
miaomiao.eat()
miaomiao.call()
2.8 super:
在 Python 面向對象編程的繼承機制里,
super()
是一個非常實用的內置函數,它主要用于調用父類(超類)的方法。super()
函數返回一個代理對象,該對象會將方法調用委托給父類或兄弟類,從而可以方便地調用父類的方法。
調用父類的構造方法
在子類的構造方法中,經常需要調用父類的構造方法來初始化從父類繼承的屬性。
class Animal:def __init__(self, name):self.name = nameprint(f"Animal 的構造方法被調用,名字是 {self.name}")class Dog(Animal):def __init__(self, name, breed):# 調用父類的構造方法super().__init__(name)self.breed = breedprint(f"Dog 的構造方法被調用,品種是 {self.breed}")# 創建 Dog 類的實例
dog = Dog("旺財", "金毛尋回犬")
在上述代碼中,Dog
類繼承自 Animal
類。在 Dog
類的構造方法 __init__
中,使用 super().__init__(name)
調用了父類 Animal
的構造方法,先完成父類屬性的初始化,再初始化子類特有的屬性。
調用父類的其他方法
除了構造方法,super()
還可以用于調用父類的其他普通方法。當子類重寫了父類的某個方法,但又需要在子類方法中使用父類方法的功能時,就可以使用 super()
來調用父類的方法。
class Animal:def speak(self):return "動物發出聲音"class Dog(Animal):def speak(self):# 調用父類的 speak 方法parent_speak = super().speak()return f"{parent_speak},具體來說是汪汪叫"# 創建 Dog 類的實例
dog = Dog()
print(dog.speak())
super()
函數的工作原理與 Python 的方法解析順序(Method Resolution Order,MRO)有關。MRO 定義了在多繼承情況下,Python 查找方法的順序。super()
會根據 MRO 來確定要調用的父類方法。可以通過類的 __mro__
屬性查看類的 MRO。
2.9 方法解析順序(MRO)
當多個父類中存在同名方法時,Python 需要確定調用哪個父類的方法,這就涉及到方法解析順序(Method Resolution Order,MRO)。可以通過類的
__mro__
屬性查看方法解析順序。
class A:def method(self):print("Method from A")class B(A):def method(self):print("Method from B")class C(A):def method(self):print("Method from C")class D(B, C):pass# 查看 D 類的方法解析順序
print(D.__mro__)# 創建 D 類的實例
d = D()
# 調用 method 方法
d.method()
在上述代碼中,D
類繼承自 B
和 C
,而 B
和 C
又都繼承自 A
。D.__mro__
會輸出一個元組,顯示方法解析的順序。當調用 d.method()
時,Python 會按照 MRO 的順序查找 method
方法,找到后就會調用該方法。
多態
3.1 概念:

3.2 基本語法:
'''
?先定義?個?類,其可能擁有多個?類對象。當我們調??個公共?法(接?)時,傳遞的對象不
同,則返回的結果不同。
'''
class Fruit(object): # ?類Fruitdef makejuice(self):print('i can make juice')
class Apple(Fruit): # ?類:蘋果# 重寫?類?法def makejuice(self):print('i can make apple juice')
class Banana(Fruit): # ?類:?蕉# 重寫?類?法def makejuice(self):print('i can make banana juice')
class Orange(Fruit): # ?類:橘?# 重寫?類?法def makejuice(self):print('i can make orange juice')
# 定義?個公共接?(專??于實現榨汁操作)
def service(obj):# obj要求是?個實例化對象,可以傳?蘋果對象/?蕉對象obj.makejuice()
# 調?公共?法
service(Orange())
如果加號的兩邊都是數值類型的數據,則加號代表運算符如果加號的兩邊傳入的是字符串類型的數據,則加號代表合并操作,返回合并后的字符串'a' + 'b' = 'ab'如果加號的兩邊出入序列類型的數據,則加號代表合并操作,返回合并后的序列[1, 2, 3] + [4, 5, 6] = [1, 2, 3, 4, 5, 6]
類屬性
類屬性是一種重要的概念,它與實例屬性共同構成了類的數據部分。類屬性是屬于類本身的屬性,它被該類的所有實例對象所共享。類屬性在類定義中直接聲明,不依賴于某個具體的實例對象。
4.1 特點:
- 共享性:所有由該類創建的實例對象都可以訪問類屬性,類屬性只有一份拷貝,存儲在類的命名空間中。
- 獨立性:類屬性獨立于實例屬性,即使沒有創建任何實例對象,類屬性依然存在于類中。
4.2 基本語法:
class Car:# 定義類屬性wheels = 4def __init__(self, brand):self.brand = brand# 通過類名訪問類屬性
print(Car.wheels) # 輸出: 4# 創建類的實例
my_car = Car("寶馬")
# 通過實例對象訪問類屬性
print(my_car.wheels) # 輸出: 4
在上述代碼中,wheels
是 Car
類的類屬性,既可以通過類名 Car
直接訪問,也可以通過類的實例對象 my_car
進行訪問。
4.3 修改類屬性
類屬性可以通過類名進行修改,修改后所有實例對象訪問該類屬性時都會得到新的值。
class Car:wheels = 4def __init__(self, brand):self.brand = brand# 修改類屬性
Car.wheels = 6# 創建類的實例
my_car = Car("寶馬")
# 通過實例對象訪問修改后的類屬性
print(my_car.wheels) # 輸出: 6
需要注意的是,如果通過實例對象去修改類屬性,實際上會為該實例創建一個同名的實例屬性,而不會影響類屬性本身。
4.4 類屬性與實例屬性的對比:
- 存儲位置:類屬性存儲在類的命名空間中,而實例屬性存儲在每個實例對象的命名空間中。
- 訪問方式:類屬性可以通過類名和實例對象訪問,實例屬性只能通過實例對象訪問。
- 數據共享性:類屬性被所有實例對象共享,一個實例對類屬性的修改會影響其他實例;實例屬性是每個實例獨有的,修改一個實例的屬性不會影響其他實例。
4.5 綜上所述
類屬性是類定義中聲明的屬性,被所有實例共享。可通過類或實例訪問修改,用于存儲類級別的數據和狀態。
類方法
類方法是綁定到類而不是實例的方法。在 Python 中,使用
@classmethod
裝飾器來定義類方法。類方法的第一個參數通常命名為cls
,它代表類本身,通過cls
可以訪問和操作類的屬性和調用類的其他方法。
5.1 基本語法
class MyClass:class_attribute = "這是類屬性"@classmethoddef class_method(cls):return cls.class_attribute# 調用類方法
result = MyClass.class_method()
print(result) # 輸出: 這是類屬性
5.2 訪問和修改類屬性
類方法可以直接訪問和修改類屬性,因為它的第一個參數 cls
代表類本身。
class Student:total_students = 0def __init__(self, name):self.name = nameStudent.total_students += 1@classmethoddef get_total_students(cls):return cls.total_students# 創建 Student 類的實例
s1 = Student("Alice")
s2 = Student("Bob")# 調用類方法獲取學生總數
total = Student.get_total_students()
print(total) # 輸出: 2
在上述代碼中,get_total_students
是一個類方法,通過 cls.total_students
訪問了類屬性 total_students
。
5.3 調用方式
類方法可以通過類名直接調用,也可以通過實例對象調用,但推薦使用類名調用,因為類方法的主要作用是操作類本身。
class MyClass:@classmethoddef class_method(cls):print("這是類方法")# 通過類名調用
MyClass.class_method()
# 通過實例調用
obj = MyClass()
obj.class_method()
5.4 與實例方法和靜態方法的對比
與實例方法對比
- 參數不同:實例方法的第一個參數是
self
,代表實例對象本身,通過self
可以訪問和操作實例屬性;類方法的第一個參數是cls
,代表類本身,通過cls
可以訪問和操作類屬性。 - 調用方式不同:實例方法必須通過實例對象調用;類方法可以通過類名或實例對象調用。
- 用途不同:實例方法主要用于操作實例對象的屬性和實現實例對象的行為;類方法主要用于操作類屬性和創建工廠方法等與類本身相關的操作。
與靜態方法對比
- 參數不同:靜態方法沒有特殊的第一個參數,它不與類或實例有直接的綁定關系;類方法有第一個參數
cls
,代表類本身。 - 功能用途不同:靜態方法通常用于執行一些與類和實例的狀態無關的通用操作,例如數學計算、字符串處理等;類方法主要用于操作類屬性、創建工廠方法等與類本身相關的操作。
5.5 綜上所述
不需要創建類的對象,通過 類名. 的?式就可以訪問類的屬性或者調用類的方法 。
用@classmethod 修飾的方法為類方法;
類方法的參數為 cls,在類方法內部通過 cls.類屬性 或者 cls.類方法 來訪問同一個類中的其他類屬性和類方法;
類方法不需要實例化就可以調用,類方法只能訪問同一個類中的類屬性和類方法
靜態方法
靜態方法是屬于類的方法,但它既不依賴于類本身(即類屬性和類方法),也不依賴于類的實例對象(即實例屬性和實例方法)。在 Python 中,使用
@staticmethod
裝飾器來定義靜態方法,靜態方法沒有類似self
(代表實例對象)或cls
(代表類本身)這樣的特殊第一個參數。既不需要訪問實例屬性或者調用實例方法也不需要訪問類屬性或者調用類方法這個時候,可以把這個方法封裝成一個靜態方法
6.1 基本語法
# 開發?款游戲
class Game(object):# 開始游戲,打印游戲功能菜單@staticmethod # 修飾符,聲明為靜態?法,?需傳遞self參數,直接使?類名即可,?需實例化對象def menu(): # 靜態?法print('1、開始游戲')print('2、游戲暫停')print('3、退出游戲')
# 開始游戲、打印菜單
Game.menu() # 調?靜態?法,?需實例化對象,直接使?類名即可
6.2 特點
- 獨立性:靜態方法不與類或實例的狀態相關聯,它不訪問類屬性、類方法,也不訪問實例屬性、實例方法。可以把它看作是一個定義在類內部的普通函數,只是為了代碼組織的需要而放在類中。
- 調用靈活性:既可以通過類名直接調用,也可以通過類的實例對象調用。
6.3 綜上所述
類里面的普通成員方法需要對象進行調用,不能直接使用類名調用。
用@staticmethod 修飾的方法為靜態方法;
靜態方法是獨立存在的,不能訪問類或者實例的任何屬性和方法;
通過 類名.靜態方法 調用靜態方法 。