qué puede parametros ejemplo depurar decoradores decorador cómo código con python python-decorators

puede - qué es un decorador en python



¿Cómo usar los decoradores de Python para verificar los argumentos de la función? (7)

Como seguramente sabrás, no es pythonic rechazar un argumento solo por su tipo.
Enfoque pitónico es más bien "tratar de lidiar con él primero"
Por eso preferiría hacer un decorador para convertir los argumentos.

def enforce(*types): def decorator(f): def new_f(*args, **kwds): #we need to convert args into something mutable newargs = [] for (a, t) in zip(args, types): newargs.append( t(a)) #feel free to have more elaborated convertion return f(*newargs, **kwds) return new_f return decorator

De esta manera, su función se alimenta con el tipo que espera, pero si el parámetro puede mostrarse como un flotador, se acepta

@enforce(int, float) def func(arg1, arg2): return arg1 * arg2 print (func(3, 2)) # -> 6.0 print (func(''3'', 2)) # -> 6.0 print (func(''three'', 2)) # -> ValueError: invalid literal for int() with base 10: ''three''

Utilizo este truco (con el método de conversión adecuado) para tratar con vectors .
Muchos métodos que escribo esperan la clase MyVector, ya que tiene muchas funcionalidades; pero alguna vez solo quieres escribir

transpose ((2,4))

Me gustaría definir algunos decoradores genéricos para verificar los argumentos antes de llamar a algunas funciones.

Algo como:

@checkArguments(types = [''int'', ''float'']) def myFunction(thisVarIsAnInt, thisVarIsAFloat) '''''' Here my code '''''' pass

Notas al margen:

  1. La comprobación de tipos está aquí para mostrar un ejemplo.
  2. Estoy usando Python 2.7, pero Python 3.0 también sería interesante

Creo que la respuesta de Python 3.5 a esta pregunta es beartype . Como se explica en este post , viene con características útiles. Tu código se vería así

from beartype import beartype @beartype def sprint(s: str) -> None: print(s)

y resultados en

>>> sprint("s") s >>> sprint(3) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<string>", line 13, in func_beartyped TypeError: sprint() parameter s=3 not of <class ''str''>


De los Decoradores para Funciones y Métodos :

def accepts(*types): def check_accepts(f): assert len(types) == f.func_code.co_argcount def new_f(*args, **kwds): for (a, t) in zip(args, types): assert isinstance(a, t), / "arg %r does not match %s" % (a,t) return f(*args, **kwds) new_f.func_name = f.func_name return new_f return check_accepts

Uso:

@accepts(int, (int,float)) def func(arg1, arg2): return arg1 * arg2 func(3, 2) # -> 6 func(''3'', 2) # -> AssertionError: arg ''3'' does not match <type ''int''>


En Python 3.3, puede usar anotaciones de funciones e inspeccionar:

import inspect def validate(f): def wrapper(*args): fname = f.__name__ fsig = inspect.signature(f) vars = '', ''.join(''{}={}''.format(*pair) for pair in zip(fsig.parameters, args)) params={k:v for k,v in zip(fsig.parameters, args)} print(''wrapped call to {}({})''.format(fname, params)) for k, v in fsig.parameters.items(): p=params[k] msg=''call to {}({}): {} failed {})''.format(fname, vars, k, v.annotation.__name__) assert v.annotation(params[k]), msg ret = f(*args) print('' returning {} with annotation: "{}"''.format(ret, fsig.return_annotation)) return ret return wrapper @validate def xXy(x: lambda _x: 10<_x<100, y: lambda _y: isinstance(_y,float)) -> (''x times y'',''in X and Y units''): return x*y xy = xXy(10,3) print(xy)

Si hay un error de validación, imprime:

AssertionError: call to xXy(x=12, y=3): y failed <lambda>)

Si no hay un error de validación, se imprime:

wrapped call to xXy({''y'': 3.0, ''x'': 12}) returning 36.0 with annotation: "(''x times y'', ''in X and Y units'')"

Puede usar una función en lugar de un lambda para obtener un nombre en el fallo de aserción.


Para imponer argumentos de cadena a un analizador que generaría errores crípticos cuando se proporcionara una entrada sin cadena, escribí lo siguiente, que intenta evitar la asignación y las llamadas de función:

from functools import wraps def argtype(**decls): """Decorator to check argument types. Usage: @argtype(name=str, text=str) def parse_rule(name, text): ... """ def decorator(func): code = func.func_code fname = func.func_name names = code.co_varnames[:code.co_argcount] @wraps(func) def decorated(*args,**kwargs): for argname, argtype in decls.iteritems(): try: argval = args[names.index(argname)] except ValueError: argval = kwargs.get(argname) if argval is None: raise TypeError("%s(...): arg ''%s'' is null" % (fname, argname)) if not isinstance(argval, argtype): raise TypeError("%s(...): arg ''%s'': type is %s, must be %s" % (fname, argname, type(argval), argtype)) return func(*args,**kwargs) return decorated return decorator


Tengo una versión ligeramente mejorada de @jbouwmans sollution, que utiliza el módulo decorador de Python, lo que hace que el decorador sea completamente transparente y mantiene no solo la firma sino también las cadenas de documentos en su lugar y podría ser la forma más elegante de usar decoradores

from decorator import decorator def check_args(**decls): """Decorator to check argument types. Usage: @check_args(name=str, text=str) def parse_rule(name, text): ... """ @decorator def wrapper(func, *args, **kwargs): code = func.func_code fname = func.func_name names = code.co_varnames[:code.co_argcount] for argname, argtype in decls.iteritems(): try: argval = args[names.index(argname)] except IndexError: argval = kwargs.get(argname) if argval is None: raise TypeError("%s(...): arg ''%s'' is null" % (fname, argname)) if not isinstance(argval, argtype): raise TypeError("%s(...): arg ''%s'': type is %s, must be %s" % (fname, argname, type(argval), argtype)) return func(*args, **kwargs) return wrapper


Todas estas publicaciones parecen estar desactualizadas: pinta ahora proporciona esta funcionalidad incorporada. Vea here . Copiado aquí para la posteridad:

Comprobación de la dimensionalidad Cuando desea que las cantidades de pinta se utilicen como entradas para sus funciones, pinta proporciona una envoltura para garantizar que las unidades sean del tipo correcto, o más precisamente, coincidan con la dimensionalidad esperada de la cantidad física.

Al igual que wraps (), puede pasar Ninguno para omitir la comprobación de algunos parámetros, pero el tipo de parámetro de retorno no está marcado.

>>> mypp = ureg.check(''[length]'')(pendulum_period)

En el formato de decorador:

>>> @ureg.check(''[length]'') ... def pendulum_period(length): ... return 2*math.pi*math.sqrt(length/G)