2016-06-19 2 views
1

Я пытаюсь понять следующее о работе с функциями и их аргументами:аргументы Понимание функции в Python

def print_my_arg(func, *args, **kwargs): 
    func(*args, **kwargs) 
    if 'my_arg' in kwargs: 
     print(' my_arg = {}'.format(kwargs['my_arg'])) 

def foo(call_no, my_arg='Default Value'): 
    print('call_no = {}'.format(call_no)) 

print_my_arg(foo, 0, my_arg='My Value 1') 
print_my_arg(foo, 1, 'My Value 2') 
print_my_arg(foo, 2) 

Выход:

call_no = 0 
    my_arg = My Value 1 
call_no = 1 # I'd like to see 'My Value 2' here 
call_no = 2 # I'd like to see 'Default Value' here 

Очевидно, что люди могут свободно вызывать функции в любом из способы, показанные выше, что заставляет меня задаться вопросом: почему my_arg не идет в kwargs в любом случае? Разве нет единого способа доступа к параметрам по имени (а не по положению), который не зависит от способа вызова функции?

Пожалуйста, обратите внимание, что:

  1. Я не заинтересован в print_my_args(func, call_no, my_arg), потому что я говорю о том случае, когда я не знаю подпись func заранее, и все же я хочу знать, если существует определенный параметр (по имени).

  2. Очевидно, что это связано с декораторами, но я написал пример более простым способом (или, надеюсь, так).

EDIT

Большое спасибо за ответы о inspect.signature. Пользуясь тем, что моя новая версия print_my_arg() является:

from inspect import signature 

def print_my_arg (func, *args, **kwargs): 
    func (*args, **kwargs) 
    sig = signature (func) 
    if 'my_arg' not in sig.parameters: return 

    binding = sig.bind (*args, **kwargs) 
    binding.apply_defaults() 

    print (" my_arg = {}".format (binding.arguments [ 'my_arg' ])) 
+1

позиционные аргументы не * "идти в "*' ** kwargs', потому что * они не являются аргументами для ключевого слова *. Если вы хотите передать второй позиционный аргумент функции, вам придется сделать это явно, совершенно неясно, почему вы ожидали другого поведения. Вы можете принудительно использовать аргументы ключевого слова, если хотите, см., Например, http://stackoverflow.com/a/37829651/3001761 – jonrsharpe

+2

Вы хотите проверить ['подпись'] (https://docs.python.org/3/library/inspect.html?highlight=signature#inspect. Signature.bind) функции для проверки имени аргумента в 'print_my_args'? то вы можете сделать 'pass_args = inspect.signature (func) .bind (* args, ** kwargs)', а затем проверить, находится ли 'my_arg' в' pass_args'. –

+0

http://stackoverflow.com/questions/3394835/args-and-kwargs –

ответ

3

не Есть равномерный способ доступа к параметрам по имени (а не положения), которое не зависит от того, как функция была вызывается?

Да по inspect ТРАЕКТОРИЯМ signature:

>>> import inspect 
>>> sig = inspect.signature(foo) 
>>> print(sig) 
(call_no, my_arg='Default Value') 
>>> args = sig.bind(1, "my_value") 
>>> args.arguments["my_arg"] 
'my_value' 

Обратите внимание, что пытается связать подпись недействительной вызов будет поднимать подобный/же TypeError, что будет поднят при вызове функции с неверными аргументами. Также аргументы, которые используют по умолчанию не будет присутствовать в args.arguments, если вы звоните args.apply_defaults()

Также обратите внимание, что ключевое слово только аргументы будут в args.kwargs словаре вместо args.arguments:

import inspect 

def bar(a,*, b=None): 
    pass 

sig = inspect.signature(bar) 

binding = sig.bind(1, b=5) 

assert "a" in binding.arguments 
assert "b" in binding.kwargs