class
)
0. 參考:1. 面向過程和面向?qū)ο蟮膮^(qū)別:B站黑馬程序員Python
成都創(chuàng)新互聯(lián)是一家專注于網(wǎng)站制作、網(wǎng)站設(shè)計與策劃設(shè)計,婺源網(wǎng)站建設(shè)哪家好?成都創(chuàng)新互聯(lián)做網(wǎng)站,專注于網(wǎng)站建設(shè)10余年,網(wǎng)設(shè)計領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:婺源等地區(qū)。婺源做網(wǎng)站價格咨詢:028-86922220
2. 類的命名方式:面向過程 可以簡單理解為 函數(shù)式編程,將某些獨立的功能封裝到函數(shù)中,然后根據(jù)步驟有順序地調(diào)用相應(yīng)的函數(shù),最終實現(xiàn)整個目的。
面向?qū)ο?就是根據(jù)最終的目的來進行職責(zé)劃分,根據(jù)職責(zé)來設(shè)計不同的類,在類中封裝不同的屬性和方法,通過類生成不同的對象,再有序地調(diào)用相應(yīng)的屬性或方法來實現(xiàn)最終目的。
3. 查看類的屬性和方法:類名要滿足大駝峰命名法,每個單詞的首字母大寫,單詞之間沒有下劃線,比如
NewStudent
。
dir(類名)
、類名.__dict__
、help(類名)
__init__
初始化方法:專門用來定義一個類具有哪些屬性。
創(chuàng)建對象時會自動調(diào)用
__init__
初始化函數(shù)。
格式:self.屬性 = 形參
"""定義一個 Animal類,屬性包括名字和年齡。"""
class Animal:
def __init__(self, new_name, age):
self.name = new_name
self.age = age
5. 封裝:根據(jù)實際需要,將屬性和方法封裝在類中。將類的實例化來創(chuàng)建對象,然后讓對象去訪問屬性或調(diào)用方法。
舉例:創(chuàng)建Solider類和Gun類,并讓Solider用Gun進行開火。
class Gun:
def __init__(self, model):
self.model = model
self.bullet_count = 0
def add_bullet(self, count):
self.bullet_count += count
def shoot(self):
if self.bullet_count<= 0:
print("[%s] No bullet..." % self.model)
return
self.bullet_count -= 1
print("[%s] shooting... [%d]" % (self.model, self.bullet_count))
class Solider:
def __init__(self, name):
self.name = name
self.gun = None
def fire(self):
if self.gun is None:
print("[%s] has No Gun" % self.name)
return
print("Go... [%s]" % self.name)
## 將 self.gun 對象對應(yīng)的方法(add_bullet和shoot)封裝到類中
self.gun.add_bullet(5)
self.gun.shoot()
if __name__ == "__main__":
ak47 = Gun("AK47")
xsd = Solider("XSD")
xsd.gun = ak47 ## 一個對象的屬性(xsd.gun)可以是另一個類創(chuàng)建的對象(ak47)
xsd.fire()
6. 私有屬性和私有方法:1. 私有屬性和私有方法都不可以在外部被直接調(diào)用。
2. 盡管如此,如果要強行訪問私有屬性和私有方法,也是可以的。具體的方法就是:實例._類名__私有屬性名或私有方法名
。
格式:__屬性名
、__方法名
舉例:
class Women(object):
def __init__(self, name):
self.name = name
self.__age = 20 ## 私有屬性,在外部不能直接訪問
def __secret(self):
## 在對象的方法內(nèi)部可以訪問對象的私有屬性
print("[%s] age is [%d]" % (self.name, self.__age))
if __name__ == "__main__":
xf = Women("XF")
## 訪問私有屬性、調(diào)用私有方法
print(xf._Women__age)
xf._Women__secret()
7. 繼承:子類 擁有 父類 的所有屬性和方法。
格式:class 類名(父類名)
繼承 具有傳遞性,即:子類 擁有 父類 以及 父類的父類 的所有屬性和方法。
當(dāng) 父類 的方法不能滿足子類的需求時,需要對方法進行重寫:
1. 覆蓋父類的方法:當(dāng)父類的方法完全不能滿足子類的需求時,可以在子類中定義一個與父類方法同名的方法。
2. 擴展父類的方法:當(dāng)父類的方法部分可以滿足子類的需求時,可以對父類方法進行擴展,具體步驟:
2.1 在子類中定義一個與父類方法同名的方法;
2.2 在該子類方法中,使用super().父類方法
來調(diào)用父類方法;
2.3 代碼其他位置可以根據(jù)子類的需求來編寫子類特有的的代碼;
重寫父類方法的舉例:
class Animal:
def bark(self):
print("Animal Bark")
class Dog(Animal):
def fly(self):
print("Dog Fly")
def bark(self):
print("Dog Bark")
if __name__ == "__main__":
xx = Dog()
xx.fly()
xx.bark()
擴展父類方法的舉例:
class Animal:
def bark(self):
print("Animal Bark")
class Dog(Animal):
def bark(self):
print("Dog GaGaGa")
super().bark()
print("!@#$%^&*")
if __name__ == "__main__":
xx = Dog()
xx.bark()
8. 繼承中的私有屬性和私有方法:子類對象 不能在自己方法內(nèi)部 直接訪問 父類的私有屬性或私有方法,但是可以 通過父類的共有方法 間接訪問到 私有屬性或私有方法。
舉例:子類對象不能在自己的方法內(nèi)部直接訪問父類的私有屬性或私有方法。
class A:
def __init__(self):
self.num1 = 100
self.__num2 = 200
def __test(self):
print("私有方法 %d %d" % (self.num1, self.__num2)) ## 在自己的方法內(nèi)部可以訪問自己的私有屬性或私有方法
class B(A):
def demo(self):
## 在子類的方法中不能訪問父類的私有屬性
print("父類私有屬性 %d" % self.__num2)
## 在子類的方法中不能調(diào)用父類的私有方法
self.__test()
if __name__ == "__main__":
b = B()
b.demo()
舉例:子類對象可以通過父類中的共有屬性或共有方法來間接訪問父類的私有屬性或私有方法。
class A:
def __init__(self):
self.num1 = 100
self.__num2 = 200
def __test(self):
print("父類的私有方法 %d %d" % (self.num1, self.__num2))
def test(self):
print("父類的私有屬性 %d" % self.__num2)
## 父類的私有方法
self.__test()
class B(A):
def demo(self):
## 通過父類的共有屬性或公有方法來訪問父類的私有屬性或私有方法
self.test()
if __name__ == "__main__":
b = B()
b.demo()
9. 多繼承:子類可以繼承多個父類,并且繼承所有父類的屬性和方法。
格式:class 子類名(父類名1, 父類名2, ...)
class A:
def test(self):
print("A --- test")
class B:
def demo(self):
print("B --- demo")
class C(A, B):
## 多繼承可以讓子類同時具有多個父類的屬性和方法
pass
if __name__ == "__main__":
c = C()
c.test()
c.demo()
多繼承的注意事項:
如果多個父類之間存在同名的屬性或方法時,盡量不要用多繼承。
多繼承中的MRO (method resolution order),主要用于多繼承時判斷 方法、屬性的調(diào)用路徑。
class A:
def test(self):
print("A --- test")
def demo(self):
print("A --- demo")
class B:
def test(self):
print("B --- test")
def demo(self):
print("B --- demo")
class C(B, A):
## 多繼承可以讓子類同時具有多個父類的屬性和方法
pass
if __name__ == "__main__":
print(C.__mro__)
## 結(jié)果:
## (,,,)
10.object
基類11. 多態(tài):
object
是所有對象的基類,提供一些內(nèi)置的屬性和方法,可以使用dir()
函數(shù)查看。Python3 中默認(rèn)以
object
為基類。
不同的子類對象調(diào)用相同的父類方法,產(chǎn)生不同的結(jié)果。
以繼承和重寫父類方法為前提。因為不同的對象都有相同的方法名,所以可以根據(jù)相同的方法名而使不同的對象產(chǎn)生不同的結(jié)果。
## 多態(tài)
class Animal(object):
def __init__(self, name):
self.name = name
def game(self):
print("Animal Play %s" % self.name)
class Dog(Animal):
def game(self):
print("Dog Play %s" % self.name)
class Person:
def __init__(self, name):
self.name = name
def game_with(self, dog):
print("%s play with %s" % (self.name, dog.name))
dog.game()
##因為 Animal 和 Dog 中都有 game方法,所以這里的dog.game()既可以是Animal中的game(),也可以是Dog中的game()
if __name__ == "__main__":
## 讓不同的子類對象調(diào)用相同的父類方法產(chǎn)生不同的結(jié)果:
## ww 和 aa 是不同的子類對象,對象xm通過調(diào)用相同的方法(game_with())來產(chǎn)生不同的結(jié)果
ww = Animal("WW")
aa = Dog("AA")
xm = Person("XM")
xm.game_with(ww)
xm.game_with(aa)
12. 類屬性和類方法:12.1 類屬性:Python中一切皆對象;
1.class AAA:
定義類對象;
2. 類對象可以擁有自己的屬性(類屬性)和方法(類方法);
3. 可以通過類名.
的方式來訪問類屬性或調(diào)用類方法;
4. 程序運行時,類對象也會加載到內(nèi)存中,但是只有一份。通過類對象可以創(chuàng)建出多個實例;
類屬性就是類對象中定義的屬性。
舉例:
## 使用類創(chuàng)建了多少個對象
class Tool(object):
count = 0
def __init__(self, name):
self.name = name
Tool.count += 1
if __name__ == "__main__":
## 創(chuàng)建工具對象
t1 = Tool("斧頭")
t2 = Tool("錘頭")
t3 = Tool("勺子")
## 輸出對象個數(shù)
print(Tool.count)
針對上述腳本有一個問題:為什么在用
Tool
創(chuàng)建實例的時候,第三行中的count=0
不會被重復(fù)調(diào)用?
我的理解是:運行程序,第一次創(chuàng)建實例時需要把class Tool
加載到內(nèi)存中,因此在加載過程中會從頭開始(包括count=0
和__init__
)。因為類對象在內(nèi)存中只需要有一份,所以后續(xù)再創(chuàng)建實例時,不需要再從頭加載,只需要運行__init__
初始化方法即可,也就是說不需要再運行count=0
,直接運行__init__
方法。因為沒有再運行count=0
,所以此時的count
對應(yīng)的數(shù)值是上一次創(chuàng)建實例時生成的結(jié)果。所以count
的數(shù)值會被累加起來。
其實針對上面有一點需要理解,就是
count += 1
會改變count
的內(nèi)存地址,如下所示:
對于
n += 1
中n
的地址發(fā)生改變的理解:
可以先理解Python中對變量a = 2
的理解,就是說a
存儲的不是整數(shù)2
而是整數(shù)2
對應(yīng)的內(nèi)存地址。當(dāng)2
就變成3
的時候,相應(yīng)的內(nèi)存地址也就發(fā)生了變化,所以a
對應(yīng)的內(nèi)存地址也就發(fā)生了變化。
由此理解n += 1
,就是說首先創(chuàng)建一個不可變對象 (整數(shù)n=初始值
),之后再把n + 1
賦給n
(內(nèi)存地址改變) ,然后回收之前的n
。
12.2 類方法Python中可變對象和不可變對象:
不可變對象:
int
,float
,str
,tuple
可變對象:字典,集合,列表,都屬于可變對象,說其可變,是指其內(nèi)存中的值可變。
舉例:
(可變對象,變量的內(nèi)存地址不變)
(不可變對象,變量的內(nèi)存地址改變)
@classmethod
:類方法就是針對類定義的方法,類方法的內(nèi)部可以直接訪問類屬性或調(diào)用其他的類方法。
格式:@classmethod
def 類方法名(cls):
1. 類方法需要
@classmethod
修飾器來標(biāo)識,告訴解釋器這是一個類方法;
2. 通過類名.
的方式來調(diào)用類方法;
3. 在方法內(nèi)部可以通過cls.
來訪問類的屬性,也可以通過cls.
來調(diào)用其他的類方法;
舉例:
## 使用類創(chuàng)建了多少個對象
class Tool(object):
count = 0
@classmethod
def show_tool_count(cls):
print("工具對象的數(shù)量 %d" % cls.count) ## 訪問當(dāng)前類的類屬性
def __init__(self, name):
self.name = name
Tool.count += 1
if __name__ == "__main__":
t1 = Tool("斧頭")
t2 = Tool("錘頭")
t3 = Tool("勺子")
## 調(diào)用 類方法
Tool.show_tool_count()
13. 靜態(tài)方法@staticmethod
:什么都不需要的時候(既不需要訪問類屬性或?qū)嵗龑傩?,也不需要調(diào)用類方法或?qū)嵗椒ǎ?,可以把這個方法封裝成靜態(tài)方法。
格式:@staticmethod
def 靜態(tài)方法名():
通過
類名.
的方式來調(diào)用靜態(tài)方法。
舉例:
class Animal(object):
@staticmethod
def fly():
print("Flying ...")
Animal.fly()
14. 案例:## 創(chuàng)建游戲類
class Game(object):
top_score = 0 ## 類屬性
def __init__(self, player_name):
self.player_name = player_name ## 實例屬性
@staticmethod
def show_help(): ## 靜態(tài)方法不需要訪問任何屬性
print("Help Information")
@classmethod
def show_top_score(cls):
print("Top Score in History : %d" % cls.top_score) ## 類方法中訪問類屬性:cls.類屬性名
def start_game(self):
print("%s Start gaming..." % self.player_name) ## 實例方法中訪問實例屬性
print("Top Score is %d" % Game.top_score) ## 實例方法中訪問類屬性:類名.類屬性名
def main():
## 1. 查看幫助信息
Game.show_help()
## 2. 顯示歷史記錄
Game.show_top_score()
## 3. 顯示玩家信息
xm = Game("Ming")
xm.start_game()
if __name__ == "__main__":
main()
15. 單例:小結(jié):
1. 實例方法 —— 方法內(nèi)部需要訪問實例屬性(方法內(nèi)部也可以用類名.類屬性
的方法來訪問類屬性)start_game()
;
2. 類方法 —— 方法內(nèi)部只需要訪問類屬性show_top_score()
;
3. 靜態(tài)方法 —— 方法內(nèi)部不需要任何屬性或方法 (show_help()
);
15.1 內(nèi)置方法單例設(shè)計模式:
用類創(chuàng)建對象,在系統(tǒng)中只有唯一一個實例。就是說,每次運行類名()
創(chuàng)建實例時,內(nèi)存地址是相同的。
實現(xiàn)方式是重寫__new__
方法。
__new__
:
類名()
創(chuàng)建實例時,Python解釋器首先會調(diào)用__new__
方法為對象分配內(nèi)存空間。__new__
是一個由object
基類提供的內(nèi)置靜態(tài)方法,主要作用是:為對象分配內(nèi)存空間;返回對象的引用。Python解釋器獲得對象的引用后,將引用作為第一個參數(shù)傳遞個__init__
方法。
15.2 重寫有一個問題就是說:為啥
__new__(cls, *args, **kwargs)
是一個靜態(tài)方法。它不是有一個cls
參數(shù)嗎?
【參考 :https://segmentfault.com/q/1010000016949076
,盡管我暫時還是看不懂】
__new__
:## __new__ 方法的重寫
class MusicPlayer(object):
def __new__(cls):
## 創(chuàng)建對象是,__new__方法會被自動調(diào)用
print("創(chuàng)建對象,分配空間")
## 分配空間
instance = super().__new__(cls) ## __new__是靜態(tài)方法,需要手動傳遞cls參數(shù)
## 返回對象的引用
return instance
def __init__(self):
print("播放器初始化")
if __name__ == "__main__":
player = MusicPlayer()
print(player)
15.3 如何實現(xiàn) 創(chuàng)建單例對象:這里重寫的
__new__
方法并不能創(chuàng)建單例對象,如下圖所示內(nèi)存地址并不相同:
15.4 如何實現(xiàn) 只執(zhí)行一次初始化動作:創(chuàng)建單例對象的話,不僅僅需要重寫
__new__
方法,而且還需要對內(nèi)存地址是否存在進行判斷。
其實上面代碼中的類屬性instance = None
就是用來判斷是否存在內(nèi)存地址的。第一次創(chuàng)建實例時,instance
通過父類中的__new__
方法被賦予了內(nèi)存地址,因此當(dāng)后續(xù)在創(chuàng)建實例時,instance
就不再是None
,也就不會再運行父類中的__new__
方法,而是直接返回類屬性instance
,由此變實現(xiàn)了后續(xù)創(chuàng)建多個實例的時候,內(nèi)存地址不會變化。
16. 內(nèi)置方法上面代碼通過設(shè)置初始化標(biāo)簽
init_flag
來判斷是否執(zhí)行過初始化動作,如果執(zhí)行過,后續(xù)運行__init__
方法的時候直接return
即可。
__del__
:作用:當(dāng)一個對象被從內(nèi)存中銷毀之前,會被自動調(diào)用;
如果希望在對象被銷毀之前再做一些事情,可以考慮__del__
方法;
Python 3.9.12 中好像沒有
__del__
內(nèi)置方法,但是用del 對象名
倒是會調(diào)用__del__
這個方法(如下結(jié)果所示:)。
上面代碼從左到右可以發(fā)現(xiàn),第一次創(chuàng)建實例時,實例不會自動銷毀。而當(dāng)調(diào)用del
方法時,會首先將第一次創(chuàng)建的實例刪掉,然后再第二次創(chuàng)建實例。而當(dāng)?shù)谌蝿?chuàng)建實例時,因為第二次創(chuàng)建的實例已經(jīng)被銷毀,因此第三次創(chuàng)建實例的時候不會先運行del
方法。
17. 內(nèi)置方法生命周期:一個對象調(diào)用
類名()
創(chuàng)建實例,聲明周期開始;一旦調(diào)用__del__
方法,生命周期結(jié)束;在對象的生命周期內(nèi),可以訪問對象屬性或者調(diào)用方法。
__str__
:返回對象的描述信息,
需要注意的是__str__
必須返回一個字符串;
使用
18. 關(guān)于機器學(xué)習(xí)中遇到的當(dāng)不想輸出上述默認(rèn)哪些信息時,可以用
__str__
來輸出特定信息(如下所示):
super(XXX, self).__init__()
的理解:參考:https://blog.csdn.net/dongjinkun/article/details/114575998 (這篇文章寫得很好)
簡言之:
super(XXX, self).__init__()
就是用父類中的初始化方法來對子類的屬性進行初始化,如下所示(以這篇文章中的例子為例):定義了兩個類:
Person
和Stu
,其中子類Stu
繼承了父類Person
。super(Stu, self).__init__(name, gender)
調(diào)用父類Person
中的初始化方法對子類Stu
中的name
和gender
屬性進行初始化,所以子類中的name
和gender
會被大寫。而school
不作處理,所以不會被大寫。
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧