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