PYTHON類型注解
函數定義的弊端
Python是動態語言,變量隨時可以被賦值,且能賦值為不同的類型
Python不是靜態編譯型語言,變量類型是在運行器決定的
動態語言很靈活,但是這種特性也是弊端
def add(x, y):
return x + y
print(add(4, 5))
print(add(‘hello’, ‘world’))
#add(4, ‘hello’)
難發現:由于不做任何類型檢查,直到運行期問題才顯現出來,或者線上運行時才能暴露出問題
難使用:函數的使用者看到函數的時候,并不知道你的函數的設計,并不知道應該傳入什么類型的數據
?如何解決這種動態語言定義的弊端呢? p
?增加文檔Documentation String p
這只是一個慣例,不是強制標準,不能要求程序員一定為函數提供說明文檔 p 函數定義更新了,文檔未必同步更新
def add(x, y):
”’
????:param?x: int
????:param?y: int
????:return: int
????”’
????return x + y
print(help(add))
print(add(4,5))
print(add(‘mag’,’edu’))
?函數注解 p Python 3.5引入
對函數的參數進行類型注解
對函數的返回值進行類型注解
只對函數參數做一個輔助的說明,并不對函數參數進行類型檢查
提供給第三方工具,做代碼分析,發現隱藏的bug
函數注解的信息,保存在__annotations__屬性中
函數參數類型檢查
思路
函數參數的檢查,一定是在函數外
函數應該作為參數,傳入到檢查函數中
檢查函數拿到函數傳入的實際參數,與形參聲明對比
?__annotations__屬性是一個字典,其中包括返回值類型的聲明。假設要做位置參數的判斷,無 法和字典中的聲明對應。使用inspect模塊
inspet模塊
提供獲取對象信息的函數,可以檢查函數和類、類型檢查
Inspect模塊
?signature(callable),獲取簽名(函數簽名包含了一個函數的信息,包括函數名、它的參數類型、它 所在的類和名稱空間及其他信息)
import inspect
def add(x:int,y:int,*args,**kwargs)->int:
return x + y
sig = inspect.signature(add)
print(sig,type(sig))?#函數簽名
print(‘params:’,sig.parameters)#有序字典
print(‘return : ‘,sig.return_annotation)
print(sig.parameters[‘x’],type(sig.parameters[‘y’]))
print(sig.parameters[‘x’].annotation)
print(sig.parameters[‘args’])
print(sig.parameters[‘args’].annotation)
print(sig.parameters[‘kwargs’])
print(sig.parameters[‘kwargs’].annotation)
?inspect.isfunction(add),是否是函數
inspect.ismethod(add)),是否是類的方法
inspect.isgenerator(add)),是否是生成器對象
inspect.isgeneratorfunction(add)),是否是生成器函數
inspect.isclass(add)),是否是類
inspect.ismodule(inspect)),是否是模塊
inspect.isbuiltin(print)),是否是內建對象
還有很多is函數,需要的時候查閱inspect模塊幫助
Parameter對象
保存在元組中,是只讀的
name,參數的名字
annotation,參數的注解,可能沒有定義
default,參數的缺省值,可能沒有定義
empty,特殊的類,用來標記default屬性或者注釋annotation屬性的空值
kind,實參如何綁定到形參,就是形參的類型
POSITIONAL_ONLY,值必須是位置參數提供
POSITIONAL_OR_KEYWORD,值可以作為關鍵字或者位置參數提供
VAR_POSITIONAL,可變位置參數,對應*args
KEYWORD_ONLY,keyword-only參數,對應*或者*args之后的出現的非可變關鍵字參數 VAR_KEYWORD,可變關鍵字參數,對應**kwargs
舉例
import inspect
def add(x:int,y:int=7,*args,z,t=10,**kwargs)->int:
return x + y
sig = inspect.signature(add)
print(sig)
print(‘params:’,sig.parameters)
print(‘return:’,sig.return_annotation)
print(‘——————————————‘)
for i,item in enumerate(sig.parameters.items()):
name,param = item
print(i+1,name,param.annotation,param.kind,param.default)
print(param.default is param.empty,end=’\n\n’)
有函數如下
def add(x, y:int=7) -> int:
return x + y p
請檢查用戶輸入是否符合參數注解的要求?
import inspect
def add(x, y:int=7) -> int:
return x + y
def check(fn):
def wrapper(*args, **kwargs):
sig = inspect.signature(fn)
params = sig.parameters
values = list(params.values())
for i,p in enumerate(args):
if isinstance(p, values[i].annotation): # 實參和形參聲明一致
print(‘==’)
for k,v in kwargs.items():
if isinstance(v, params[k].annotation): # 實參和形參聲明一致
print(‘===’)
return fn(*args, **kwargs)
return wrapper
?
import inspect
def check(fn):
def wrapper(*args, **kwargs):
sig = inspect.signature(fn)
params = sig.parameters
values = list(params.values())
for i,p in enumerate(args):
param = values[i]
if param.annotation is not param.empty and not isinstance(p, param.annotation):
print(p,’!==’,values[i].annotation)
for k,v in kwargs.items():
if params[k].annotation is not inspect._empty and not isinstance(v, params[k].annotation):
print(k,v,’!===’,params[k].annotation)
return fn(*args, **kwargs)
return wrapper
@check
def add(x, y:int=7) -> int:
return x + y
Functools模塊
partial方法
偏函數,把函數部分的參數固定下來,相當于為部分的參數添加了一個固定的默認值,形成一 個新的函數并返回 p
從partial生成的新函數,是對原函數的封裝
import functools
def add(x,y) -> int:
return x + y
newadd = functools.partial(add,y=5)
print(newadd(7))
print(newadd(7,y=6))
print(newadd(y=8,x=9))
import inspect
print(inspect.signature(newadd))
舉例
import functools
def add(x,y,*args)-> int:
print(args)
return x+y
newadd = functools.partial(add,1,2,3,4)
print(newadd(7))
print(newadd(7,10))
#print(newadd(9,10,y=20,x=26))
print(newadd())
import inspect
print(inspect.signature(newadd))
?
import functools
def partial(func, *args, **keywords):
def newfunc(*fargs, **fkeywords): # 包裝函數
newkeywords = keywords.copy()
newkeywords.update(fkeywords)
return func(*(args + fargs), **newkeywords)
newfunc.func = func # 保留原函數
newfunc.args = args # 保留原函數的位置參數
newfunc.keywords = keywords # 保留原函數的關鍵字參數參數
return newfunc
def add(x,y):
return x+y
foo = partial(add,4)
foo(5)
@functools.lru_cache(maxsize=128, typed=False) p
Least-recently-used裝飾器。lru,最近最少使用。cache緩存 p
如果maxsize設置為None,則禁用LRU功能,并且緩存可以無限制增長。當maxsize是二的冪 時,LRU功能執行得最好 p
如果typed設置為True,則不同類型的函數參數將單獨緩存。例如,f(3)和f(3.0)將被視為具有不 同結果的不同調用
import functools
import time
@functools.lru_cache()
def add(x,y,z=3):
time.sleep(z)
return x + y
print(add(4,5))
print(add(4.0,5))
print(add(4, 6))
print(add(4, 6, 3))
print(add(6, 4))
print(add(4, y=6))
print(add(x=4, y=6))
ptint(add(y=6, x=4))
舉例:打印斐波那契數列
import functools
@functools.lru_cache()
def add(n):
if n <3:
return 1
return add(n-1)+add(n-2)
print(add(35))
?lru_cache裝飾器應用
使用前提
同樣的函數參數一定得到同樣的結果
函數執行時間很長,且要多次執行
本質是函數調用的參數=>返回值
缺點
不支持緩存過期,key無法過期、失效 p
不支持清除操作
不支持分布式,是一個單機的緩存
適用場景,單機上需要空間換時間的地方,可以用緩存來將計算變成快速的查詢
本文來自投稿,不代表Linux運維部落立場,如若轉載,請注明出處:http://www.www58058.com/97339