2016-06-19 19 views
1

Ich versuche, die folgende über den Umgang mit Funktionen und ihre Argumente zu verstehen:Legendes Funktionsargumente in 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) 

Ausgang:

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 

Offensichtlich Menschen sind frei Funktionen entweder aufzurufen von die oben gezeigten Möglichkeiten, die mich wundern: warum my_arg geht nicht auf kwargs sowieso? Gibt es keinen einheitlichen Weg, um Parameter über den Namen (und nicht über die Position) zu erreichen, was nicht davon abhängt, wie die Funktion aufgerufen wurde?

Bitte beachten Sie, dass:

  1. ich in print_my_args(func, call_no, my_arg) interessiert mich nicht, weil ich über den Fall zu sprechen bin, wo ich weiß nicht, die Unterschrift von func im Voraus und doch möchte ich wissen, ob ein bestimmter Parameter existiert (nach Name).

  2. Klar ist, dass an Dekorateure im Zusammenhang, aber ich habe das Beispiel in einer einfacheren Art und Weise geschrieben (oder hoffentlich).

EDIT

Vielen Dank für die Antworten zu inspect.signature. Mit, dass meine neue Version von print_my_arg() ist:

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

Positional Argumente nicht * "go to "*' ** kwargs', weil * sie keine Schlüsselwort-Argumente * sind. Wenn Sie das zweite positionale Argument an die Funktion übergeben wollen, müssen Sie dies explizit tun, es ist überhaupt nicht klar, warum Sie ein anderes Verhalten erwartet haben. Sie könnten Schlüsselwortargumente erzwingen, wenn Sie wirklich möchten, siehe z. http://StackOverflow.com/a/37829651/3001761 – jonrsharpe

+2

Möchten Sie die [Signatur] (https://docs.python.org/3/library/inspect.html?highlight=signature#inspect. Signature.bind) der Funktion zum Überprüfen des Argumentnamens in 'print_my_args'? dann könntest du 'passed_args = inspect.signature (func) .bind (* args, ** kwargs)' dann prüfen, ob 'my_arg' in' passed_args' ist. –

+0

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

Antwort

3

Gibt es nicht eine einheitliche Parameter für den Zugriff durch Namen (und nicht durch Position), die auf dem Weg hängt nicht die Funktion war aufgerufen?

Yes inspect ing die 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' 

Beachten Sie, dass die Unterschrift auf einen ungültigen Aufruf zu binden versuchen, werden ähnliche/gleiche Typeerror erhöhen, die ausgelöst werden würde, wenn die Funktion mit ungültigen Argumenten aufrufen. Auch Argumente, die den Standard verwenden werden in args.arguments nicht vorhanden sein, es sei denn, Sie args.apply_defaults()

auch dieses Schlüsselwort beachten Sie rufen nur Argumente in args.kwargs Wörterbuch werden statt 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