之前項目需求,需要通過反射獲取函數的參數,python中可以通過函數簽名(signature)來實現。
首先需要了解函數參數的類型,Python的參數類型一共有5種:POSITIONAL_OR_KEYWORD、VAR_POSITIONAL、VAR_KEYWORD、KEYWORD_ONLY、POSITIONAL_ONLY
其中 POSITIONAL_OR_KEYWORD、VAR_POSITIONAL、VAR_KEYWORD、KEYWORD_ONLY 比較常用
參數類型為VAR_POSITIONAL時,即*args參數,只能通過位置傳值,如
def say_hello(*args): print('hello {0}'.format(args)) # 通過位置傳值 say_hello('jack', 'tom')
參數類型為VAR_KEYWORD,即 **kwargs參數,只能通過關鍵字傳值,如
def func_b(**kwargs): print(kwargs) # 通過關鍵字傳值 func_b(a=1, b=2)
參數的類型為POSITIONAL_OR_KEYWORD時,說明此參數前面沒有VAR_POSITIONAL類型的參數,可以通過位置或關鍵字傳值,如
def say_hello(name): print('hello {0}'.format(name)) # 通過位置傳值 say_hello('jack') # 通過關鍵字傳值 say_hello(name='tom')
參數類型為KEYWORD_ONLY時,說明此參數前面存在VAR_POSITIONAL類型的參數,只能通過關鍵字傳值,如
def func_b(*args, a, b): print(args, a, b) # 只能通過關鍵字傳值 func_b('test', a=1, b=2)
比較特別的是POSITIONAL_ONLY,只能通過位置傳值的參數。Python並沒有明確的語法去定義一個只能通過位置傳值的函數參數,但是在很多內置和擴展模塊的函數會接受這種類型的參數。
實際獲取函數參數時,需要用到inspect模塊,通過這個模塊的signature方法獲取函數簽名。
import inspect def func_a(arg_a, *args, arg_b='hello', **kwargs): print(arg_a, arg_b, args, kwargs) if __name__ == '__main__': # 獲取函數簽名 func_signature = inspect.signature(func_a) func_args = [] # 獲取函數所有參數 for k, v in func_signature.parameters.items(): # 獲取函數參數后,需要判斷參數類型 # 當kind為 POSITIONAL_OR_KEYWORD,說明在這個參數之前沒有任何類似*args的參數,那這個函數可以通過參數位置或者參數關鍵字進行調用 # 這兩種參數要另外做判斷 if str(v.kind) in ('POSITIONAL_OR_KEYWORD', 'KEYWORD_ONLY'): # 通過v.default可以獲取到參數的默認值 # 如果參數沒有默認值,則default的值為:class inspect_empty # 所以通過v.default的__name__ 來判斷是不是_empty 如果是_empty代表沒有默認值 # 同時,因為類本身是type類的實例,所以使用isinstance判斷是不是type類的實例 if isinstance(v.default, type) and v.default.__name__ == '_empty': func_args.append({k: None}) else: func_args.append({k: v.default}) # 當kind為 VAR_POSITIONAL時,說明參數是類似*args elif str(v.kind) == 'VAR_POSITIONAL': args_list = [] func_args.append(args_list) # 當kind為 VAR_KEYWORD時,說明參數是類似**kwargs elif str(v.kind) == 'VAR_KEYWORD': args_dict = {} func_args.append(args_dict) print(func_args)