2011-07-05 6 views
5

Per un decoratore che sto scrivendo mi piacerebbe manipolare una specifica di nome parametro di una funzione. Si consideri il seguente decoratore:Come posso trattare argomenti posizionali come argomenti chiave in Python 2

def square_param(param): 
    def func_decorator(func): 
     def func_caller(*args,**kwargs): 
      kwargs[param] = kwargs[param] * kwargs[param] 
      return func(*args,**kwargs) 
    return func_caller 
return func_decorator 

applicata sulla funzione successiva:

@square_param('dividend') 
def quotient(divisor=1,dividend=0): 
    return dividend/divisor 

Ciò funzionerà se dividendo è chiamato come ad esempio l'argomento parola chiave:

>>> quotient(dividend=2) 
4 

Tuttavia, quando somministrato come un argomento posizionale questo fallirà.

>>> quotient(3,4) 
TypeError: quotient() got multiple values for keyword argument 'dividend' 

Con Python 3 ho potuto risolvere questo forzando il parametro da always given as a keyword:

@square_param('dividend') 
def quotient(divisor=1,*,dividend=0): 
    return dividend/divisor 

ma vorrei sostenere Python 2 e anche io vorrebbe mettere come piccoli restrizioni sulla funzione .

C'è un modo che io possa risolvere questo comportamento a mio decoratore?

+3

Esaminare il modulo ispezionare. Potrebbe avere quello che ti serve per questo. Dai un'occhiata a getargspec. – Dirk

risposta

3

In primo luogo, la vostra square_param decoratore non funziona perché non restituisce le funzioni. Ha bisogno di essere:

def square_param(param): 
    def func_decorator(func): 
     def func_caller(*args,**kwargs): 
      kwargs[param] = kwargs[param] * kwargs[param] 
      return func(*args,**kwargs) 
     return func_caller 
    return func_decorator 

Ora ho preso @ consiglio di Dirk e guardò nel modulo inspect. Puoi farlo controllando prima se il parametro è uno degli argomenti posizionali della funzione, e in secondo luogo se tale argomento posizionale è stato specificato, e quindi modificando la posizione dell'argomento. È inoltre necessario assicurarsi di modificare solo kwargs se il parametro è stato fornito come argomento della parola chiave.

import inspect 

def square_param(param): 
    def func_decorator(func): 
     def func_caller(*args,**kwargs): 
      funparams = inspect.getargspec(func).args 
      if param in funparams: 
       i = funparams.index(param) 
       if len(args) > i: 
        args = list(args) # Make mutable 
        args[i] = args[i] * args[i] 
      if param in kwargs: 
       kwargs[param] = kwargs[param] * kwargs[param] 
      return func(*args,**kwargs) 
     return func_caller 
    return func_decorator 
+0

Grazie, questo è esattamente quello che stavo cercando! Riguardo il ritorno delle funzioni, quello era davvero un piccolo errore stupido fatto in questo esempio. –

2

anche senza l'utilizzo di ispezionare possiamo ottenere la funzione params

>>> func = lambda x, y, args: (x, y, {}) 
>>> func.func_code.co_argcount 
3 
>>> func.func_code.co_varnames 
('x', 'y', 'args') 
+0

Grazie! Potresti forse aggiungere un link dove sono documentati i membri func_code? Non riesco a trovarlo. –

+0

Prego! "func_code è l'oggetto codice che rappresenta il corpo della funzione compilato." È possibile fare riferimento alla sezione "tipi Callable" su [link] (http://docs.python.org/reference/datamodel.html) per maggiori dettagli. – Yajushi