PYTHON類型注解

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

(0)
zhangmengzhangmeng
上一篇 2018-05-02
下一篇 2018-05-02

相關推薦

  • Python第二周小結

    不知不覺已經正式學習接觸Python兩周了,第二周主要開始了Python內置數據結構的學習,包括從一開始的列表list,元組tuple,字符串string,再到后來的bytes, bytearray, 以及最后的集合set。這些數據結構可以說Python最為基礎的幾種類型,想要用Python寫出漂亮的代碼離不開對他們的熟練掌握與深刻理解。這幾個結構各有各的特…

    Python筆記 2018-03-31
  • Python高階函數及裝飾器

    First Class Object 函數在Python中是一等公民 函數也是對象,可調用的對象 函數可作為普通變量、參數、返回值等 高階函數 數學定義:y=g(f(x)) 高階函數需滿足的條件,至少其一 接受一個或多個函數作為參數 輸出一個函數 內建函數的高階函數 排序:sorted(iterable[,key][,reverse]) 返回一個新列表,對一…

    2018-04-22
  • Python內置數據結構——字符串

    知識結構圖 學習筆記 字符串 字符組成的有序序列,字符的集合 使用單引號、雙引號、三引號引起來的字符序列 不可變對象 Unicode類型 定義 單引號、雙引號、三引號 r、R:引號內的字符原樣輸出 元素訪問 索引訪問 可迭代 join連接 “string“.join(iteratable) 使用string作為分隔符將可迭代對象連接起…

    2018-03-31
  • 楊輝三角專題

    楊輝三角;二項式

    2018-04-09
欧美性久久久久