2016-04-28 2 views
3

Ich fand eine seltsame Instanz, wenn ich versuche, einen Dekorateur zu testen. Code wie folgt:Wird der Standardwert der Python-Parameter von args oder kwargs akzeptiert?

def doublefunc(fn): 
    def warpped(*arg, **kwargs): 
     arg = (i if type(i) is not int else i*2 for i in arg) 
     for k, v in kwargs.iteritems(): 
      kwargs[k] = v *2 if type(v) is int else v 
     return fn(*arg, **kwargs) 
    return warpped 

@doublefunc 
def add(x, y, m, z=10, v='bbaa'): 
    print 'x=%s, y=%s, m=%s, z=%s, v=%s' % (x, y, m, z, v) 
    return x + y + z 

wenn ich rufe Funktion wie folgt:

print add(1, 1, 'teststring') 

Der zurückgegebene Wert ist aber nicht 24, dass der Standardwert des Parameters z in Funktion Addiermittel, wird nicht in Decorator Funktion inner akzeptiert. Warum?

+0

'print add (1, 1, 'teststring')' gibt ** 12 ** zurück! Und es ist richtig! Sind Sie sicher, dass Sie ** 14 ** erhalten haben? Warum erwartest du 24? – EbraHim

+0

@EbraHim Es gibt 14 auf meinem Computer (Python 2.7, Ubuntu Kylin). Verwenden Sie Python 3? –

+0

Sorry, meine Schuld. Es gibt 14. – EbraHim

Antwort

3

Dies ist schwierig, aber man kann mit einigem Nutzen des inspect Modul

import inspect 

def get_unpassed_defaults(fn, args, kwargs): 
     args_len = len(args) + len(kwargs) 
     argspec = inspect.getargspec(fn) 

     defaults = argspec.defaults 
     default_names = argspec.args[-len(defaults):] 

     number_of_args = len(argspec.args) 
     number_of_positional_args = number_of_args - len(argspec.defaults) 
     number_of_kwargs = number_of_args - number_of_positional_args 
     number_of_kwargs_passed_as_positional = len(args) - number_of_positional_args 

     default_values = defaults[number_of_kwargs_passed_as_positional:] 
     default_names = default_names[number_of_kwargs_passed_as_positional:] 
     defaults_dict = dict(zip(default_names, default_values)) 

     for kwarg_name in kwargs: 
      if kwarg_name in defaults_dict: 
       defaults_dict.pop(kwarg_name) 

     return defaults_dict 

Und dann nutzen diese in doublefunc unpassen Standardargumente erhalten:

def doublefunc(fn): 
    def wrapped(*arg, **kwargs): 
     unpassed_defaults = get_unpassed_defaults(fn, arg, kwargs) 
     kwargs.update(unpassed_defaults) 

     arg = (i if type(i) is not int else i*2 for i in arg) 
     for k, v in kwargs.iteritems(): 
      kwargs[k] = v *2 if type(v) is int else v 

     return fn(*arg, **kwargs) 
    return wrapped 

@doublefunc 
def add(x, y, m, z=10, v='bbaa'): 
    print 'x=%s, y=%s, m=%s, z=%s, v=%s' % (x, y, m, z, v) 
    return x + y + z 

print add(1, 1, "teststring", v=3) 

Gibt:

x=2, y=2, m=teststring, z=20, v=6 
24 
+0

Danke für Ihre Hilfe, aber wie Vanza sagte, Dies ist eine doppelte Frage. Der innere Warpper des Dekorators hat keinen Zugriff auf die Warp-Funktion. Danke auch Vanza! –

+0

@HenryJohn aber der innere Wrapper des Dekorators * hat * Zugang wie ich gezeigt habe. Mein Code macht was du willst. –

2

Der Grund dafür ist, dass Decorator-Funktion nicht für inn suchen er Attribute (in diesem Fall default kwargs) der umschlossenen Funktion. Wenn Sie das noch tun wollen, müssen Sie nur mit Wörterbüchern spielen.

Wird nicht mit Python 2 arbeiten, weil es keine getfullargspec Methode ist, so dass für @Nolen Lizenz Antwort

Für Python 3

>>> import inspect 
>>> def doublefunc(fn): 
     def wrapped(*arg, **kwargs): 
      defaults = (inspect.getfullargspec(fn).kwonlydefaults) 
      defaults.update(kwargs) 
      kwargs = defaults 
      arg = (i if type(i) is not int else i*2 for i in arg) 
      for k, v in kwargs.items(): 
       kwargs[k] = v *2 if type(v) is int else v 
      return fn(*arg, **kwargs) 
     return wrapped 

>>> @doublefunc 
def add(x, y, m, *args, z=10, v='bbaa'): 
    print('x={0}, y={1}, m= {2}, z={3}, v={4}'.format(x,y,m,z,v)) 
    return x + y + z 

>>> print(add(1, 1, 'teststring', v=1)) 

Ergebnis folgen:

x=2, y=2, m= teststring, z=20, v=2 
24 
+1

Sie können (und sollten) Klammern mit print-Anweisungen in Python 2 verwenden, damit der Code in beiden funktioniert. Für kleine Wörterbücher können Sie einfach ".items()" in beiden auch verwenden, es wird nur eifrig in 2 und faul in 3 sein. Für große Wörterbücher importiere ich normalerweise 'iteritems' aus der Bibliothek' six'. – Paul

+1

'Getfullargspec' existiert nicht in Python 2. –

+0

Danke, es ist also nur eine Lösung für Python 3. @Nolen Royalty –

Verwandte Themen