一.類
1.類的定義:
????????????????class 類名:
???????????????????????“”“注釋 ”“”
????????????????????????pass
2.實例的創建:
實例 = 類名(parameterlist)
parameterlist:定義類時__init__()方法的參數,如果該方法只有一個self參數,parameterlist可以省略
class Goose():"""定義了鵝類"""passgoose = Goose()
print(goose)#<__main__.Goose object at 0x00000229213C6900>
3.創建類的構造函數方法:__init__()方法
????????定義類時,通常會包含一個__init__()方法。該方法是構造函數方法,每當創建一個類的新實例時,Python都會自動執行它。
1.__init__()方法用來初始化新創建對象,在一個對象被創建以后會立即調用。
2. __init__()方法必須包含一個self參數,并且必須是第一個參數。self參數是一個指向實例本身的引用,用于訪問類中的屬性和方法。(相當于JAVA中的this)
? ? ? ?3.當__init__()方法只有一個參數時,在創建類的實例時,不需要指定實際參數。
4.__init__()方法的名稱是以兩個連續下劃線開頭和結尾,這是一種約定,用于區分Python默認方法和普通方法。
????????5._init_()方法可以分為無參構造方法和有參構造方法。當使用無參構造方法創建對象時,所有對象的屬性都有相同的初始值;當使用有參構造為法創建對象時,對象的屬性可以有不同的初始值;
????????6.如果你寫多個構造方法,但是你在調用的時候,后寫的構造方法會取代前面的構造方法(無論這兩者參數上是否有區別),這點與JAVA不一樣,所以一般一個類只有一個構造方法。
????????7.一般來說,構造方法里面寫初始化內容
class Goose():def __init__(self):print ("self:",self) #self: <__main__.Goose object at 0x0000022CD8766900>print("type of self:",type(self))#type of self: <class '__main__.Goose'>print("我是鵝類,我會自動執行該語句")
g=Goose()
在__init__()方法中,除了self參數外,還可以自定義一些參數,參數間使用逗號“,”進行分隔。
class Goose:'''鵝類'''def __init__(self,beak,wing,claw):print("我是鵝類!我有以下特征:")print(beak)print(wing)print(claw)
beak_1 = "喙的基部較高,長度和頭部的長度幾乎相等"
wing_1 = "翅膀長而尖"
claw_1 = "爪子是蹼壯的"
wildGoose = Goose(beak_1,wing_1,claw_1)
當然,也可以不用構造函數(一般不推薦這樣)
class Rect1():def getPeri(self,a,b):print("周長為:",(a+b)*2)def getArea(self,a,b):print("面積為:",a*b)
r1=Rect1()
r1.getPeri(3,4)
r1.getArea(3,4)
print(r1.__dict__)class Rect2():def __init__(self,a,b):self.a=aself.b=bdef getPeri(self):print("周長為:",(self.a+self.b)*2)def getArea(self):print("面積為:",self.a*self.b)
r2=Rect2(5,6)
r2.getPeri()
r2.getArea()
print(r2.__dict__)
二.類的成員
1.類的成員,靜態定義
定義屬性并訪問
數據成員是指在類中定義的變量,即屬性,根據定義位置,又可以分為類屬性和實例屬性。
① 類屬性
類屬性是指定義在類中,并且在函數體外的屬性。
類屬性可以在類的所有實例之間共享值,也就是在所有實例化的對象中公用。
類屬性可以通過類名稱或者實例名訪問。
class Goose:'''鵝類'''neck = "脖子較長"wing = "振翅頻率高"leg = "腿位于身體的中心支點,行走自如"def __init__(self):print("我屬于鵝類!我有以下特征:")print(self.neck)print(self.wing)print(self.leg)
geese = Goose()
2.動態添加屬性
在Python中除了可以通過類名稱訪問類屬性,還可以動態地為類和對象添加屬性
class Goose:'''鵝類'''neck = "脖子較長"wing = "振翅頻率高"leg = "腿位于身體的中心支點,行走自如"def __init__(self):print("我屬于鵝類!我有以下特征:")print(Goose.neck)print(Goose.wing)print(Goose.leg)geese = Goose()
#在類外,動態的為對象添加屬性
geese.beak = "喙的基部較高,長度和頭部的長度幾乎相等"
print("鵝的喙:",geese.beak)
3.實例屬性
實例屬性,在JAVA中類似于對象的屬性(官方不是這個叫法),實例屬性是指在類的方法中的屬性,就是__init__()構造方法中的參數,只作用于當前實例中。
class Goose:'''鵝類'''neck = "脖子較長"wing = "振翅頻率高"leg = "腿位于身體的中心支點,行走自如"def __init__(self,name):self.name = nameprint("我屬于鵝類!我有以下特征:")print(self.neck)print(self.wing)print(self.leg)print("實例屬性,geese特有:",name)geese = Goose("娃哈哈")
4.實例屬性和類屬性的區別
class Dog:# 類屬性(所有狗共享)species = "Canis familiaris"def __init__(self, name):# 實例屬性(每個狗獨立)self.name = name# 創建兩個實例
dog1 = Dog("Buddy")
dog2 = Dog("Max")# 訪問類屬性(通過類名或實例名)
print(Dog.species) # 輸出: Canis familiaris
print(dog1.species) # 輸出: Canis familiaris# 訪問實例屬性(只能通過實例名)
print(dog1.name) # 輸出: Buddy
print(dog2.name) # 輸出: Max# 嘗試通過類名訪問實例屬性(報錯!)
#print(Dog.name) # AttributeError: type object 'Dog' has no attribute 'name'# 修改類屬性
Dog.species = "Canis lupus"
print(dog1.species) # 輸出: Canis lupus(所有實例同步更新)
print(dog2.species) # 輸出: Canis lupus# 通過實例修改"類屬性"(實際是創建同名實例屬性)
dog1.species = "Golden Retriever"
print(dog1.species) # 輸出: Golden Retriever(僅影響dog1)
print(dog2.species) # 輸出: Canis lupus(dog2不受影響)# 類屬性本身未被修改
print(Dog.species) # 輸出: Canis lupus
總結如下:
特性 | 類屬性 | 實例屬性 |
---|---|---|
定義位置 | 類內部,__init__ ?方法之外 | 通常在?__init__ ?方法內 |
歸屬 | 屬于類 | 屬于實例 |
訪問方式 | 類名或實例名均可訪問 | 只能通過實例名訪問 |
共享性 | 所有實例共享 | 每個實例獨立 |
修改影響 | 修改后影響所有實例 | 修改僅影響當前實例 |
5.訪問限制
????????為了保證類內部的某些屬性或方法不被外部所訪問,可以在屬性或方法名前面添加單下劃線(_foo)、雙下劃線(__foo)或首尾加雙下劃線(__foo__),從而限制訪問權限。
其中,單下劃線、雙下劃線、首尾雙下劃線的作用如下:
首尾雙下劃線表示定義特殊方法,一般是系統定義名字,如__init__(),一般自定義不推薦這種定義
以單下劃線開頭的表示protected(保護)類型的成員,只允許類本身和子類進行訪問,但
不能使用“from module import *”語句導入
雙下劃線表示private(私有)類型的成員,只允許定義該方法的類本身進行訪問,而且也
不能通過類的實例進行訪問,但是可以通過“類的實例名._類名_xxx”方式訪問。
class MyClass:def __init__(self):self.public_value = "Public" # 公開屬性,可任意訪問self._protected_value = "Protected" # 保護屬性(單下劃線開頭)self.__private_value = "Private" # 私有屬性(雙下劃線開頭)def public_method(self):print("This is a public method.")def _protected_method(self):print("This is a protected method.")def __private_method(self):print("This is a private method.")def __special_method__(self):print("This is a special method (magic method).")class SubClass(MyClass):"""繼承MyClass類"""def show_protected(self):print("接受保護屬性 :", self._protected_value)def try_private(self):# 直接訪問私有屬性會報錯# print(self.__private_var) # 報錯:AttributeError# 但可以通過 _類名__私有屬性 的方式訪問print("Accessing private from subclass:", self._MyClass__private_var)# 實例化
obj = MyClass()
sub_obj = SubClass()# 1. 公開成員(無下劃線)
print(obj.public_value) # 輸出: Public
obj.public_method() # 輸出: This is a public method.# 2. 保護成員(單下劃線開頭)
print(obj._protected_value) # 輸出: Protected(可以訪問但不推薦)
obj._protected_method() # 輸出: This is a protected method.
sub_obj.show_protected() # 輸出: Accessing protected from subclass: Protected# 3. 私有成員(雙下劃線開頭)
# print(obj.__private_var) # 報錯:AttributeError
# obj.__private_method() # 報錯:AttributeError
print(obj._MyClass__private_var) # 輸出: Private(通過名稱重整訪問)
obj._MyClass__private_method() # 輸出: This is a private method.
sub_obj.try_private() # 輸出: Accessing private from subclass: Private# 4. 特殊方法(首尾雙下劃線)
obj.__special_method__() # 輸出: This is a special method (magic method).
三.類的方法
1.實例方法
????????實例方法是指在類中定義的函數。該函數是一種在類的實例上操作的函數。同__init__()方法一樣,實例方法的第一個參數必須是self,并且必須包含一個self參數.
語法:
def 方法名(self,參數1,參數2....):
方法內容
調用形式:
? ? ? ? 只能通過類名.方法名(參數)
class Goose:def __init__(self,beak,wing,claw):print("我是鵝類!我有以下特征:")print(beak)print(wing)print(claw)def fly(self,state):print(state)
'''*******************調用方法**********************'''
beak_1 = "喙的基部較高,長度和頭部的長度幾乎相等"
wing_1 = "翅膀長而尖"
claw_1 = "爪子是蹼壯的"
wildGoose = Goose(beak_1,wing_1,claw_1)
wildGoose.fly("我飛行的時候,一會排成個人字,一會排成個一字")
2.類方法:
在該方法前面必須加上@classmethod(注解,裝飾器),來表明這是一個類方法,且必須帶一個參數cls(跟類方法的self一樣,但是為了區分兩者,這里要寫作cls)
調用方式:
????????推薦? ? ?類名.方法名調用,
????????不推薦? ?對象名.方法名
作用:
用于操作類屬性(而不是實例屬性)
常用于工廠方法(創建類的不同實例)。
class Dog:"""species為一個類屬性@classmethod后的類方法一般為了操作類屬型(調用修改等等)"""species = "Canine" # 類屬性def __init__(self, name):self.name = name@classmethoddef get_species(cls):return cls.species # 訪問類屬性@classmethoddef from_birth_year(cls, name, birth_year):age = 2025 - birth_yearreturn cls(name) # 返回一個新的 Dog 實例,并將name傳給新的實例(畢竟建造一個實例需要參數)# 通過類名調用類方法
print(Dog.get_species()) # 輸出: Canine# 類方法作為工廠方法創建實例
dog = Dog.from_birth_year("Max", 2018)
print(dog.name) # 輸出: Max
注意:return cls(name)意味著
# 返回一個新的 Dog 實例,并將name傳給新的實例(畢竟建造一個實例需要參數)
3..靜態方法
在該方法前面必須加上@staticmethod(注解),來表明這是一個靜態方法,可以無參數,
調用方式:
????????推薦? ? ?類名.方法名調用,
????????不推薦? ?對象名.方法名
作用:
與類相關,但不依賴類或實例的狀態(即不訪問?
self
?或?cls
)。類似于普通函數,但邏輯上屬于類。
class MathUtils:@staticmethoddef add(a, b):return a + b@staticmethoddef multiply(a, b):return a * b# 通過類名調用靜態方法
print(MathUtils.add(3, 7)) # 輸出: 10# 也可以實例化后調用(但不推薦,靜態方法不依賴實例)
utils = MathUtils()
print(utils.multiply(2, 4)) # 輸出: 8
4.特殊方法:
(1)析構方法__del__()
Python中的垃圾回收主要采用的是引用計數。引用計數是一種內存管理技術,它通過引用計數器記錄所有對象的引用數量,當對象的引用計數器數值為0時,就會將該對象視為垃圾進行回收。
????????getrefcount()函數是 sys模塊中用于統計對象引用數量的函數,其返回結果通常比預期的結果大1。這是因為getrefcount()函數也會統計臨時對象的引用。當一個對象的引用計數器數值為0時,就會調用_del__()方法,這個方法就是類的析構方法。系統就會銷毀這個對象,收回對象所占用的內存空間。
所以析構方法(即__del_()方法)是用于銷毀對象時系統自動調用的方法。每個類中也都默認有一個__del__()目方法,可以顯式地定義析構方法。
? ? ? ? 注意:析構方法不是銷毀方法,而是在銷毀之前釋放資源,銷毀方法是靠py底層代碼來完成
(2)__str__()
簡單來說就是相當于JAVA中的toString方法
若是不重寫,就會用py自帶的方法
class Person:def __init__(self, name, age):self.name = nameself.age = agep = Person("Alice", 25)
print(p) # 輸出: <__main__.Person object at 0x7f8b1c1b3d90>
若是重寫:
class Person:def __init__(self, name, age):self.name = nameself.age = agedef __str__(self):return f"Person(name={self.name}, age={self.age})"p = Person("Alice", 25)
print(p) # 輸出: Person(name=Alice, age=25)
四.繼承:
跟Java一樣有object類為總的父類
1.單繼承
基本語法:
????????class 子類名(父類名)
# 父類
class Animal:def speak(self):print("動物發出聲音")def eat(self):print("父類動物在吃東西")# 子類
class Dog(Animal): # Dog繼承Animaldef speak(self): # 重寫父類方法print("汪汪汪!")# 使用
animal = Animal()
animal.speak() # 輸出:動物發出聲音dog = Dog()
dog.speak() # 輸出:汪汪汪!
dog.eat() #子類繼承父類方法eat
? ? ? ??
super關鍵字編程super方法,用法相同
2.多繼承
1.在職研究生繼承了Student和staff類
2.兩者都有showinfo方法,調用對象.方法名,會優先調用第一個繼承的父類 的方法,這里即Student類的showinfo方法
若想調用
Teacher
的方法,可以:調整繼承順序:
class Person(Teacher, Student)
直接指定:
Teacher.showinfo(p)
在子類中重寫方法并手動選擇調用哪個父類的方法是
3. 方法重寫(Override)
子類可以重寫父類的方法以改變其行為
# 父類
class Animal:def speak(self):print("動物發出聲音")def eat(self):print("父類動物在吃東西")# 子類
class Dog(Animal): # Dog繼承Animaldef speak(self): # 重寫父類方法print("汪汪汪!")# 使用
animal = Animal()
animal.speak() # 輸出:動物發出聲音dog = Dog()
dog.speak() # 輸出:汪汪汪!
4. super()函數
# 父類
class Animal:def speak(self):print("動物發出聲音")def eat(self):print("父類動物在吃東西")# 子類
class Dog(Animal): # Dog繼承Animaldef speak(self): # 重寫父類方法print("汪汪汪!")super().eat()# 使用
dog = Dog()
dog.speak() # 輸出:汪汪汪!
5.強制轉換
????????對于Python繼承機制
class Animal:def eat(self):print("動物吃東西")class Dog(Animal): # Dog繼承自Animaldef eat(self):print("狗吃骨頭")def bark(self):print("汪汪叫")class Cat(Animal):def eat(self):print("貓吃魚")
# 向上轉型(自動) - 子類轉父類
animal = Dog() # 狗是動物(自動轉換)
animal.eat() # 輸出: 狗吃骨頭def trans(animal,obj):if isinstance(animal, obj):dog = animal dog.bark() else:print("不能將動物轉為動物子類")trans(animal,Dog)
"""狗吃骨頭
汪汪叫
"""
dog = Dog()
cat = Cat()
trans(cat,Dog)#不能將動物轉為動物子類
? ? ? ? 動物= 狗可以,動物=貓 可以,但是不能? ?狗=? 動物
????????要想狗 = 動物必須強制轉換,狗 = (狗)動物
class Animal:def __init__(self, name):self.name = nameclass Dog:def __init__(self, animal): # 構造函數中實現轉換self.name = animal.namedef bark(self):print(f"{self.name}在汪汪叫")animal = Animal("旺財")
dog = Dog(animal) # 強制將Animal轉為Dog
dog.bark() # 輸出: 旺財在汪汪叫
五.多態
首先Python中的多態根JAVA不一樣,JAVA的多態必須要在繼承的基礎之上,但是py的多態,根本不關心你是不是繼承,是不是相同的數據類型。只要不同的類中有相同的方法,即可實現多態。
所以要定義一個接口,實現類的多態化。
class Animal:def eat(self):print("人吃五谷雜糧")class Dog:def eat(self):print("狗吃骨頭")class Cat: def eat(self):print("貓吃魚")def jiekou(obj):obj.eat()# 創建實例
animal = Animal()
dog = Dog()
cat = Cat()# 多態調用
jiekou(animal) # 輸出: 人吃五谷雜糧
jiekou(dog) # 輸出: 狗吃骨頭
jiekou(cat) # 輸出: 貓吃魚
?