# 靜態語言:編譯過程中會檢查,會發下問題
# 動態語言:只有在運行時才會發現問題
# 1 由於編譯期間不做任何檢查,直到運行期間我呢提才會暴露
# 2 函數使用者並不知道參數類型的時候,容易傳錯參數類型
# 函數注解:給函數的形參一個類型,並有指向的return值
# python3.5開始引入
# 可以用於第三方工具(如:pycharm)提示是否有return
# 3.6可以對變量進行注解,如:i:int = 3
def add(x:int, y:int) -> int: # 聲明規范是整數相加,並不強制形參類型
'''
This is add function
:param x: int
:param y: int
:return: int
'''
return x + y
print(add(4, 10))
print(add(4.0, 5.0))
print(add('abc', 'd'))
# 業務應用
# 檢查實參類型
# __annotations__是一個字典,其中包括返回值類型的聲明。
# inspect模塊:提供回去對象信息的函數,可以檢查函數和類、類型檢查
# signature(callable)函數,可以獲取簽名(函數簽名包含了一個函數的信息,包括:函數名、參數類型、函數所在的類和名稱空間及其他信息)
import inspect
def add(x:int, y:int, *args, **kwargs) -> int:
'''
This is add function
:param x: int
:param y: int
:return: int
'''
return x + y
print('0 ', add.__annotations__) # 普通字典
sig = inspect.signature(add) # 拿到add函數簽名((x:int, y:int, *args, **kwargs) -> int)
print('---------------------')
print(sig)
print('1 params: ', sig.parameters) # 返回OrderedDict(有序字典)
print('2 return: ', sig.return_annotation) # 返回值注解
print('3 ', sig.parameters['x'])
print('4 ', sig.parameters['x'].annotation) # 參數注解
print('5 ', sig.parameters['args'])
print('6 ', sig.parameters['args'].annotation) # 空類型 inspect._empty
print('7 ', sig.parameters['kwargs'])
print('8 ', sig.parameters['kwargs'].annotation) # 空類型 inspect._empty
# inspect模塊
# inspect.isfunction(add) 是否是函數
# inspect.ismethod(add) 是否是類的方法
# inspect.isgenerator(add) 是否是生成器對象
# inspect.isgeneratorfunction(add) 是否是生成器函數
# inspect.isclass(add) 是否是類
# inspect.ismodule(inspect) 是否是模塊
# inspect.isbuiltin(print) 是否是內建對象
# inspect模塊 - parameter對象
# 保存在元組中,只讀 OrderedDict([('x', <Parameter "x:int">), ('y', <Parameter "y:int">), ('args', <Parameter "*args">), ('kwargs', <Parameter "**kwargs">)])
# name 參數名
# annotation 參數注解,可能沒有定義
# default 參數缺省值,可能沒有定義
# empty 特殊的類,用來標記default屬性或者注釋annotation屬性的空值
# kind 實參如何綁定到形參
# POSITIONAL_ONLY 值必須是位置參數提供
# POSITIONAL_OR_KEYWORD 值可以作為關鍵字或者位置參數提供
# VAR_POSITIONAL 可變位置參數,對應*args
# KEYWORD_ONLY keyword_only參數,對應*或者*args之后出現的非可變關鍵字參數
# VAR_KEYWORD 可變關鍵字參數,對應**kwargs
def add2(x, y:int=11, *args, z, t=10, **kwargs) -> int:
return x + y
sig = inspect.signature(add2)
print('================================')
print('1 ', sig)
print('2 params ', sig.parameters)
print('3 return ', sig.return_annotation)
for i, item in enumerate(sig.parameters.items()):
name, param = item
print(i+1, name, '\n', param.annotation, '\n', param.kind, '\n', param.default)
print(param.default is param.empty, end='\n\n')
# 裝飾器
from functools import wraps
def checker(fn):
@wraps(fn) # Decorator factory
def wrapper(*args, **kwargs):
# 檢查實參
print(args, kwargs)
sig = inspect.signature(fn)
params = sig.parameters # 有序字典
# 位置參數檢查
param_list = tuple(params.keys()) # 轉換成有序的元組
for i, v in enumerate(args):
k = param_list[i] # 拿到key
if isinstance(v, params[k].annotation): # 根據key拿到注解
print(v, ' is ', params[k].annotation)
else:
print(v, ' is not ', params[k].annotation)
# 關鍵參數檢查
for k,v in kwargs.items():
if isinstance(v, params[k].annotation):
print(v, ' is ', params[k].annotation)
else:
errstr = '{} {} {}'.format(v, 'is not', params[k].annotation)
print(errstr)
raise TypeError(errstr) # 拋出異常
result = fn(*args, **kwargs)
return result
return wrapper
@checker
def add3(x:int, y:int=8) -> int:
return x + y
print('================================')
print(add3(4, y=5))
add3('abc', 'bcd')
print(add3(4))