以下內容基於python 3x
涉及的知識前提:
建議理解python裝飾器後學習此內容
函式註解概述
函式註解可以針對函式的引數、返回值新增元資料,其為註解。
python是動態語言,變數賦值時不會強制宣告型別,且能隨時重新賦值。無法像靜態編譯型語言一樣,在編譯時發現基本問題。
函式的引數要求,沒有詳細的doc string或更新沒跟上,以至後續的使用者不能夠清晰明白設計者要求的引數型別。以上行為導致的出錯率變高,難以使用等問題。
函式註解可以對引數的要求型別進行註解,對函式返回值進行註解;其只是對做乙個輔助性的說明,並不強制進行型別檢查。
一些第三方工具(pycharm)會對已定義的函式註解進行檢查標記提示等,在編寫**時同樣也可以通過編寫乙個裝飾器,來進行一些檢查。
實際應用
inspect模組
註解資訊儲存在函式的__annotations__屬性中,函式的引數檢查,一定是在函式實際執行之前,檢查傳遞進來的形參與原有的註解型別是否匹配。
__annotations__屬性是乙個字典,對於位置引數的判斷,較為複雜,可直接使用inspect模組獲取函式簽名資訊。
# 註解x和y引數要求為int型別,返回結果為int型別
# syntax:
def foo(x: int, y: int) -> int:
return x + y
檢視其__annotations__屬性資訊:
def foo(x: int, y: int) -> int:
return x + y
print(type(foo.__annotations__))
print(foo.__annotations__)
# 輸出結果:
使用inspect模組獲取簽名資訊:
inspect.signature返回的是乙個ordereddict(有序字典)
import inspect
def foo(x: int, y: int) -> int:
return x + y
sig = inspect.signature(foo)
print(sig.parameters)
# 輸出結果:
ordereddict([('x', ), ('y', )])
使用parameter方法,獲取更詳細的資訊:
import inspect
def foo(x: int , y: int, z=20) -> int:
return x + y + z
sig = inspect.signature(foo)
par = sig.parameters
print(par.items())
print(par["x"].name, "|-|", par["x"].annotation, "|-|", par["x"].default, "|-|", par["x"].empty, "|-|", par["x"].kind)
print(par["y"].name, "|-|", par["y"].annotation, "|-|", par["y"].default, "|-|", par["y"].empty, "|-|", par["y"].kind)
print(par["z"].name, "|-|", par["z"].annotation, "|-|", par["z"].default, "|-|", par["z"].empty, "|-|", par["z"].kind)
#輸出結果:
odict_items([('x', ), ('y', ), ('z', )])
x |-| |-| |-| |-| positional_or_keyword
y |-| |-| |-| |-| positional_or_keyword
z |-| |-| 20 |-| |-| positional_or_keyword
parameter輸出結果的資訊儲存在元組中,唯讀;
name:獲取引數名字;
annotation:獲取引數的註解,有可能沒有定義,則為_empty;
default:返回引數的預設值,如其中z引數的20即為預設值;
empty:特殊的類,用來標記default屬性或者注釋annotation屬性的空值;
kind:實參如何繫結到形參。以下是形參的型別:
positional_only,值必須是位置引數提供的(python中並沒有絕對的位置引數,未實現)。
positional_or_keyword,值可以作為關鍵字或者未知引數提供。
var_positional,可變位置引數,對應*args。
keyword_only,keyword-only引數,對應*args之後出現的非可變關鍵字引數。
var_keyword,可變關鍵字引數,對應**kwargs。
業務**
了解以上方法後,編寫乙個函式裝飾器用來檢測傳參是否規範。
首先在函式執行之前,獲取到函式簽名、及簽名的parameter資訊將parameter資訊。
kv結構的value轉換為列表儲存。
使用for迴圈檢查每個對應的parameter內引數(實際傳的引數)註解是否不為空並且同時是否不是已知型別(isinstance函式可以檢查傳的值是否是已知型別,返回true或false,簽名中引數的annotation型別為已知的)。
匹配通過,則說明傳遞的引數和註解要求型別一致,否則則丟擲typeerror型別的自定義錯誤資訊。
import inspect
import functools
def parameter_check(fn):
@functools.wraps(fn)
sig = inspect.signature(fn)
parameter = sig.parameters
value = list(parameter.values())
for i, par in enumerate(args):
if value[i].annotation is not value[i].empty and not isinstance(par, value[i].annotation):
raise typeerror("parameter type error")
for k, v in kwargs.items():
if parameter[k].annotation is not parameter[k].empty and not isinstance(v, parameter[k].annotation):
raise typeerror("parameter type error")
ret = fn(*args, **kwargs)
return ret
@parameter_check
def foo(x: int, y: int, z=20) -> int:
return x + y + z
print(foo(1, 50, 29))
# 輸出結果:
# 更改傳參,檢視效果:
print(foo("hhh", 50, 29))
# 輸出結果:
traceback (most recent call last):
file "e:/project/python/test.py", line 29, in
print(foo("hhh", 50, 29))
raise typeerror("parameter type error")
typeerror: parameter type error
總結通過對函式註解的了解,能夠更加規範**,提高效率,避免出錯。在實際應用中亦可方便的展開使用。
函式報錯注入 盲注
即利用系統輸出的報錯資訊來進行注入 floor報錯注入 floor報錯注入應該是報錯注入中最為複雜的一種了 其中的基礎為下面這句話 1select count floor rand 0 2 x from security.users group by x 需要注意的時候floor後面的x為乙個別名,...
連續函式註記
這是我在兩年前寫的一點東西,現在稍微整理一下,刪去了錯誤的內容,貼到這裡.乙個函式在某一點處連續的定義是 lim f x f a 這條式子說的是 對於任意給定的 varepsilon 0 都存在 delta 0 使得 x a delta 時都有 f x f a varepsilon 函式在某一點處連...
連續函式註記
這是我在兩年前寫的一點東西,現在稍微整理一下,刪去了錯誤的內容,貼到這裡.乙個函式在某一點處連續的定義是 lim f x f a 這條式子說的是 對於任意給定的 varepsilon 0 都存在 delta 0 使得 x a delta 時都有 f x f a varepsilon 函式在某一點處連...