Escribe automáticamente parámetros de conversión en Python

Fondo:
La mayoría de las veces ejecuto scripts de Python desde la línea de comandos en tuberías, por lo que mis argumentos siempre son cadenas que se deben convertir al tipo apropiado. Hago muchos pequeños scripts cada día y el tipo de conversión de cada parámetro para cada script lleva más tiempo del que debería.

Pregunta:
¿Hay una forma canónica de escribir automáticamente parámetros de conversión para una función?

Mi manera:
He desarrollado un decorador para hacer lo que quiero si no hay una manera mejor. El decorador es el autocast fxn a continuación. El fxn decorado es fxn2 en el ejemplo. Tenga en cuenta que al final del bloque de código pasé 1 y 2 como cadenas y si ejecuta el script, los agregará automáticamente. ¿Es esta una buena manera de hacer esto?

def estimateType(var): #first test bools if var == 'True': return True elif var == 'False': return False else: #int try: return int(var) except ValueError: pass #float try: return float(var) except ValueError: pass #string try: return str(var) except ValueError: raise NameError('Something Messed Up Autocasting var %s (%s)' % (var, type(var))) def autocast(dFxn): '''Still need to figure out if you pass a variable with kw args!!! I guess I can just pass the dictionary to the fxn **args?''' def wrapped(*c, **d): print c, d t = [estimateType(x) for x in c] return dFxn(*t) return wrapped @autocast def fxn2(one, two): print one + two fxn2('1', '2') 

EDITAR: Para cualquier persona que venga aquí y quiera la versión actualizada y concisa, vaya aquí:

https://github.com/sequenceGeek/cgAutoCast

Y aquí también está la versión de trabajo rápida basada en lo anterior:

 def boolify(s): if s == 'True' or s == 'true': return True if s == 'False' or s == 'false': return False raise ValueError('Not Boolean Value!') def estimateType(var): '''guesses the str representation of the variables type''' var = str(var) #important if the parameters aren't strings... for caster in (boolify, int, float): try: return caster(var) except ValueError: pass return var def autocast(dFxn): def wrapped(*c, **d): cp = [estimateType(x) for x in c] dp = dict( (i, estimateType(j)) for (i,j) in d.items()) return dFxn(*cp, **dp) return wrapped ######usage###### @autocast def randomFunction(firstVar, secondVar): print firstVar + secondVar randomFunction('1', '2') 

Si quieres convertir automáticamente los valores:

 def boolify(s): if s == 'True': return True if s == 'False': return False raise ValueError("huh?") def autoconvert(s): for fn in (boolify, int, float): try: return fn(s) except ValueError: pass return s 

Puede ajustar boolify para aceptar otros valores booleanos si lo desea.

Puede usar la evaluación plana para ingresar una cadena si confía en la fuente:

 >>> eval("3.2", {}, {}) 3.2 >>> eval("True", {}, {}) True 

Pero si no confía en la fuente, podría usar literal_eval del módulo ast.

 >>> ast.literal_eval("'hi'") 'hi' >>> ast.literal_eval("(5, 3, ['a', 'b'])") (5, 3, ['a', 'b']) 

Edición: Como comentario de Ned Batchelder, no aceptará cadenas no citadas, así que agregué una solución alternativa, también un ejemplo sobre el decorador de autocaste con argumentos de palabras clave.

 import ast def my_eval(s): try: return ast.literal_eval(s) except ValueError: #maybe it's a string, eval failed, return anyway return s #thanks gnibbler def autocaste(func): def wrapped(*c, **d): cp = [my_eval(x) for x in c] dp = {i: my_eval(j) for i,j in d.items()} #for Python 2.6+ #you can use dict((i, my_eval(j)) for i,j in d.items()) for older versions return func(*cp, **dp) return wrapped @autocaste def f(a, b): return a + b print(f("3.4", "1")) # 4.4 print(f("s", "sd")) # ssd print(my_eval("True")) # True print(my_eval("None")) # None print(my_eval("[1, 2, (3, 4)]")) # [1, 2, (3, 4)] 

Me imagino que puede hacer un sistema de firma de tipo con un decorador de funciones, como el que tiene, solo uno que toma argumentos. Por ejemplo:

 @signature(int, str, int) func(x, y, z): ... 

Tal decorador se puede construir con bastante facilidad. Algo como esto (EDITAR – funciona!):

 def signature(*args, **kwargs): def decorator(fn): def wrapped(*fn_args, **fn_kwargs): new_args = [t(raw) for t, raw in zip(args, fn_args)] new_kwargs = dict([(k, kwargs[k](v)) for k, v in fn_kwargs.items()]) fn(*new_args, **new_kwargs) return wrapped return decorator 

Y así, ¡ahora puede imbuir funciones con firmas de tipo!

 @signature(int, int) def foo(x, y): print type(x) print type(y) print x+y >>> foo('3','4')   7 

Básicamente, esta es una versión de tipo explícito del método de @ utdemir.

Si está analizando los argumentos desde la línea de comandos, debe usar el módulo argparse (si está usando Python 2.7).

Cada argumento puede tener un tipo esperado, por lo que saber qué hacer con él debe ser relativamente sencillo. Incluso puedes definir tus propios tipos.

… con bastante frecuencia, la cadena de línea de comandos debe interpretarse como otro tipo, como un flotante o int. El argumento de palabra clave de tipo add_argument () permite que se realicen las comprobaciones de tipo y las conversiones de tipo necesarias. Los tipos y funciones incorporados comunes se pueden usar directamente como el valor del argumento de tipo:

 parser = argparse.ArgumentParser() parser.add_argument('foo', type=int) parser.add_argument('bar', type=file) parser.parse_args('2 temp.txt'.split()) >>> Namespace(bar=, foo=2) 

Hay un par de problemas en tu fragmento.

 #first test bools if var == 'True': return True elif var == 'False': return False 

Esto siempre buscará True porque estás probando contra las cadenas 'True' y 'False' .

No hay una coerción automática de tipos en python. Tus argumentos cuando recibes vía *args y **kwargs pueden ser cualquier cosa. Primero buscará una lista de valores (cada uno de los cuales puede ser cualquier tipo de datos, primitivo y complejo) y, segundo, buscará un mapeo (con cualquier mapeo válido posible). Por lo tanto, si escribe un decorador, terminará con una buena lista de verificaciones de errores.

Normalmente, si desea enviar en str, justo cuando se invoca la función, enciérrelo en string a través de ( str ) y envíelo.

Sé que llegué tarde a este juego, pero ¿qué tal eval?

 def my_cast(a): try: return eval(a) except: return a 

o alternativamente (y más seguro):

 from ast import literal_eval def mycast(a): try: return literal_eval(a) except: return a