類(lèi)編寫(xiě)細(xì)節(jié)
1.class 語(yǔ)句
class語(yǔ)句細(xì)節(jié)
- python的class語(yǔ)句是屬于OOP的一種工具(即定義變量名的工具续挟,將數(shù)據(jù)和邏輯暴露給客戶(hù)端),而不是聲明式的
- class語(yǔ)句是對(duì)象的創(chuàng)建者蔬啡,類(lèi)似于對(duì)象工廠(chǎng)
- class語(yǔ)句是一種隱含的賦值運(yùn)算,即執(zhí)行class語(yǔ)句時(shí)镀虐,會(huì)產(chǎn)生類(lèi)對(duì)象并且將其引用存儲(chǔ)到定義的類(lèi)名稱(chēng)上
- class語(yǔ)句與def一樣星爪,都是可執(zhí)行語(yǔ)句,即python還沒(méi)有執(zhí)行到class語(yǔ)句時(shí)粉私,類(lèi)是不存在的
- class是復(fù)合語(yǔ)句顽腾,所有種類(lèi)語(yǔ)句都可以位于其主體內(nèi),如print, 賦值語(yǔ)句, if, def...
class語(yǔ)句如何得到命名空間
- 首先,執(zhí)行類(lèi)語(yǔ)句的時(shí)候诺核,會(huì)從頭至尾執(zhí)行其主體內(nèi)的所有語(yǔ)句
- 其次抄肖,是在執(zhí)行過(guò)程中的賦值運(yùn)算會(huì)在這個(gè)類(lèi)作用域中創(chuàng)建變量名,從而成為對(duì)應(yīng)的類(lèi)對(duì)象屬性
- 與函數(shù)相比,可以把class語(yǔ)句看成一個(gè)本地作用域,在class語(yǔ)句下定義的變量就屬于這個(gè)本地作用域
- 與模塊相比,定義的變量名是可以共享的并且成為當(dāng)前類(lèi)的對(duì)象屬性
class語(yǔ)句一般形式
## 根據(jù)上述所言窖杀,className是類(lèi)對(duì)象的一個(gè)引用
class className(superclass1,superclass2,...):
'''
定義類(lèi)屬性,屬于所有實(shí)例的共享數(shù)據(jù),通過(guò)類(lèi)語(yǔ)句下進(jìn)行定義和創(chuàng)建
'''
class_attr = value
'''
定義實(shí)例方法以及實(shí)例屬性
'''
def method(self,data): ## 定義實(shí)例方法
self.attr = data ## 設(shè)置實(shí)例屬性,通過(guò)帶有self的方法來(lái)分配屬性信息
2.方法
實(shí)例方法對(duì)象調(diào)用等價(jià)于類(lèi)方法函數(shù)調(diào)用
## python自動(dòng)將實(shí)例方法的調(diào)用自動(dòng)轉(zhuǎn)成類(lèi)方法函數(shù),并傳遞實(shí)例對(duì)象作為第一個(gè)參數(shù)傳遞
class Person:
def study(self,name):
print("%s study method in for %s" % (name,self.__class__.__name__)
>>> p = Person()
>>> p.study("keithl")
keithl study method in for Person
>>> Person.study(p,"keithl")
keithl study method in for Person
## instance.method(arg1,arg2,...) == class.method(instance,arg1,arg2,...)
調(diào)用超類(lèi)的構(gòu)造函數(shù)
__init__
方法
class Person:
def __init__(self):
print("call person init ....")
class Student(Person):
pass
>>> s = Student() ## 創(chuàng)建子類(lèi)時(shí)會(huì)調(diào)用父類(lèi)構(gòu)造函數(shù),原因是子類(lèi)沒(méi)有定義自己的構(gòu)造函數(shù)
call person init ....
## 為子類(lèi)增加構(gòu)造函數(shù)
class Student(Person):
def __init__(self):
print("call student init ....")
>>> s = Student() ## 只輸出子類(lèi)的__init__方法,并沒(méi)有調(diào)用父類(lèi)方法,原因在于python是根據(jù)命名空間來(lái)執(zhí)行調(diào)用方法
call student init ....
## 若要調(diào)用父類(lèi)構(gòu)造方法則必須顯示進(jìn)行調(diào)用
class Student(Person):
"""
必須在子類(lèi)構(gòu)造函數(shù)中顯式調(diào)用父類(lèi)的構(gòu)造函數(shù),并傳遞子類(lèi)的self引用
"""
def __init__(self):
print("call student init start....")
Person.__init__(self)
print("call student init end....")
>>> s = Student()
call student init start....
call person init ....
call student init end....
靜態(tài)方法
- 使用場(chǎng)景:
- 目標(biāo):為所有類(lèi)實(shí)例提供數(shù)據(jù)共享的類(lèi)屬性
- 執(zhí)行:通過(guò)類(lèi)名稱(chēng)訪(fǎng)問(wèn)類(lèi)屬性
- 優(yōu)化:其一是使用OOP思想封裝類(lèi)屬性而對(duì)外提供方法,其二是考慮擴(kuò)展性,通過(guò)繼承來(lái)定制
- 落地:使用靜態(tài)方法或者類(lèi)方法,即不需要傳遞類(lèi)對(duì)象self實(shí)例參數(shù)的方法
## person.py
class Person:
num = 1
"""
定義一個(gè)沒(méi)有帶參數(shù)的普通方法
"""
def printNum():
Person.num += 1
print("the number is %s" % Person.num)
printNum = staticmethod(printNum) ## 聲明為靜態(tài)方法
"""
定義一個(gè)帶參數(shù)的普通方法,此參數(shù)為類(lèi)對(duì)象參數(shù)
"""
def clsPrintNum(cls):
Person.num += 1
print("the number is %s" % Person.num)
clsPrintNum = classmethod(clsPrintNum) ## 聲明為類(lèi)方法
>>> Person.printNum()
the number is 2
>>> Person.clsPrintNum()
the number is 3
## person.py 使用裝飾器來(lái)聲明靜態(tài)或類(lèi)方法
class Person:
num = 1
@staticmethod
def printNum():
Person.num += 1
print("the number is %s" % Person.num)
@classmethod
def clsPrintNum(cls):
Person.num += 1
print("the number is %s" % Person.num)
靜態(tài)方法漓摩、類(lèi)方法與實(shí)例方法
- 類(lèi)中帶有實(shí)例對(duì)象self的參數(shù)傳遞的方法稱(chēng)為實(shí)例方法
- 類(lèi)中帶有類(lèi)對(duì)象cls的參數(shù)傳遞的方法并通過(guò)函數(shù)classmethod或者裝飾器@classmethod聲明的方法稱(chēng)為類(lèi)方法
- 類(lèi)中沒(méi)有實(shí)例對(duì)象self和類(lèi)對(duì)象cls參數(shù)傳遞的方法,且通過(guò)staticmethod或裝飾器@staticmethod什么的方法稱(chēng)為靜態(tài)方法
class Person:
@staticmethod
def static_method():
print("static method ...")
@classmethod
def class_method(cls):
print("class method ....")
def instance_method(self):
print("instance method ...")
'''
python3.x可以調(diào)用下面的函數(shù)入客,可以說(shuō)是靜態(tài)方法,但嚴(yán)格意義上是屬于類(lèi)的一個(gè)行為方法管毙,但是python2.x無(wú)法該方法
'''
def fn():
print("just a fn,if py3.x,it is static method")
## 總結(jié):
1)在類(lèi)中定義方法一定要規(guī)范化,明確是靜態(tài)方法還是類(lèi)方法抑或是實(shí)例方法
2)避免使用最后一種方式在類(lèi)中定義方法
3.命名空間與作用域
- 命名空間:用于記錄變量的軌跡,key是變量名稱(chēng),value是變量值,作用就是根據(jù)變量名稱(chēng)搜索變量
- 使用無(wú)點(diǎn)號(hào)運(yùn)算的變量名稱(chēng)(X),將根據(jù)LEGB(local/enclosing/global/builtin)作用域查找法則來(lái)搜索變量
- 使用點(diǎn)號(hào)的屬性名稱(chēng)(object.x)使用的是對(duì)象命名空間來(lái)搜索變量(對(duì)象:類(lèi)的實(shí)例對(duì)象和類(lèi)對(duì)象)
- 有些作用域會(huì)對(duì)對(duì)象的命名空間進(jìn)行初始化(模塊和類(lèi))
無(wú)點(diǎn)號(hào)運(yùn)算的變量名稱(chēng)
- 賦值語(yǔ)句:在當(dāng)前作用域創(chuàng)建或更改變量X,除非聲明為全局變量
X = "global X"
def enclosing_fn():
## global X
X = "enclosing fn" ## 創(chuàng)建當(dāng)前enclosing_fn的本地變量X如果沒(méi)有聲明為全局變量的話(huà)
- 引用:根據(jù)LEGB作用域法則來(lái)搜索變量
X = "global X"
def enclosing_fn():
X = "enclosing fn" ## 如果注釋此行,將打印全局的變量X
print(X)
def local_x()
x = "local x" ## 如果僅注釋此行,將會(huì)打印嵌套的變量X
print(x)
local_x()
點(diǎn)號(hào)的屬性變量名稱(chēng)
- 賦值語(yǔ)句:在對(duì)應(yīng)的對(duì)象命名空間中創(chuàng)建或修改屬性名稱(chēng)X,即object.X = value
>>> p = Person()
## 在對(duì)象實(shí)例的命名空間創(chuàng)建或更改屬性名稱(chēng)name
p.name = "keithl" ## 并無(wú)進(jìn)行變量名稱(chēng)的搜索
## 在類(lèi)的命名空間中創(chuàng)建或更改屬性名稱(chēng)name
Person.name = "keithl" ## 并無(wú)進(jìn)行變量名稱(chēng)的搜索
- 引用
- 基于類(lèi)的對(duì)象引用:會(huì)在對(duì)象內(nèi)搜索屬性名稱(chēng)X,若沒(méi)有找到則根據(jù)繼承搜索來(lái)查找
- 基于模塊對(duì)象的引用:先導(dǎo)入模塊,再?gòu)哪K中讀取X
>>> p = Person()
>>> p.name ## 從對(duì)象命名空間開(kāi)始按照繼承樹(shù)來(lái)搜索
>>> Person.name ## 從類(lèi)的命名空間開(kāi)始按照繼承樹(shù)來(lái)搜索
命名空間字典
- 模塊的命名空間是以字典的形式實(shí)現(xiàn)的,并且可以由屬性
__dict__
來(lái)顯示 - 類(lèi)和對(duì)象可以看成一個(gè)帶有鏈接的字典,屬性點(diǎn)號(hào)就是字典索引運(yùn)算,屬性繼承就是搜索鏈接的字典
- 實(shí)例與類(lèi)通過(guò)
__class__
屬性鏈接 - 類(lèi)與超類(lèi)通過(guò)
__bases__
屬性鏈接,可以通過(guò)遞歸往上遍歷超類(lèi)
- 實(shí)例與類(lèi)通過(guò)
- 都可以通過(guò)
__dict__
查看模塊桌硫、類(lèi)或者對(duì)象的屬性信息
類(lèi)與模塊的關(guān)系總結(jié)
-
類(lèi)
- 調(diào)用類(lèi)會(huì)創(chuàng)建新的對(duì)象
- 由class來(lái)創(chuàng)建類(lèi)對(duì)象
- 通過(guò)調(diào)用來(lái)使用
- 屬于模塊的一部分
-
模塊
- 是數(shù)據(jù)和邏輯包
- 通過(guò)py抑或其他語(yǔ)言來(lái)擴(kuò)展
- 必須導(dǎo)入才能使用