2016-06-19 1 views
1

J'essaie de comprendre les éléments suivants concernant les fonctions et leurs arguments:arguments de la fonction Comprendre en 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) 

Sortie:

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 

Il est évident que les gens sont libres d'invoquer des fonctions dans l'une des les moyens montrés ci-dessus, ce qui me fait me demander: pourquoi my_arg ne va pas à kwargs de toute façon? N'y a-t-il pas une manière uniforme d'accéder aux paramètres par nom (et non par position), qui ne dépend pas de la façon dont la fonction a été invoquée?

S'il vous plaît noter que:

  1. Je ne suis pas intéressé par print_my_args(func, call_no, my_arg), parce que je parle le cas où je ne connais pas la signature de func à l'avance et pourtant je veux savoir si un paramètre particulier existe (par nom). Il est clair que c'est lié aux décorateurs, mais j'ai écrit l'exemple d'une manière plus simple (ou je l'espère).

EDIT

Un grand merci pour les réponses sur inspect.signature. En utilisant, ma nouvelle version de print_my_arg() est:

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

arguments positionnels ne * "vont "*' ** kwargs', parce que * ce ne sont pas des mots-clés *. Si vous voulez passer le second argument positionnel à la fonction, vous devrez le faire explicitement, il n'est pas du tout clair pourquoi vous attendiez un comportement différent. Vous pouvez forcer des arguments de mots clés si vous le souhaitez vraiment, voir par exemple. http://stackoverflow.com/a/37829651/3001761 – jonrsharpe

+2

voulez-vous inspecter la ['signature'] (https://docs.python.org/3/library/inspect.html?highlight=signature#inspect. Signature.bind) de la fonction pour vérifier le nom de l'argument dans 'print_my_args'? alors vous pourriez faire 'passed_args = inspect.signature (func) .bind (* args, ** kwargs)' alors vérifiez si 'my_arg' est dans' passed_args'. –

+0

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

Répondre

3

est pas là une façon uniforme pour accéder aux paramètres par nom (et non par la position), qui ne dépend pas de la façon dont la fonction était invoqué?

Oui par inspect ing la 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' 

Notez que d'essayer de lier la signature à un appel invalide soulèvera même/même TypeError que soulèverait lors de l'appel de la fonction avec des arguments non valides. En outre des arguments qui utilisent la valeur par défaut ne seront pas présents dans args.arguments sauf si vous appelez args.apply_defaults()

Notez également que seuls arguments mot-clé seront dans le dictionnaire args.kwargs au lieu de 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