在Python中自动类型转换参数
背景:
我通常在命令行中运行Python脚本,所以我的参数总是字符串,需要转换成合适的类型。我每天都会写很多小脚本,每次都要为每个脚本的参数进行类型转换,这样花的时间比我想象的要多。
问题:
有没有一种标准的方法可以自动将函数的参数转换类型?
我的方法:
如果没有更好的方法,我开发了一个装饰器来实现我想要的功能。这个装饰器就是下面的autocast函数。被装饰的函数是示例中的fxn2。请注意,在代码块的最后,我将1和2作为字符串传入,如果你运行这个脚本,它会自动将它们相加。这种做法好吗?
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')
编辑:对于任何来到这里并想要更新和简洁的工作版本的人,请访问这里:
https://github.com/sequenceGeek/cgAutoCast
这里还有一个基于上述内容的快速工作版本:
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')
6 个回答
6
我想你可以用一个函数装饰器来创建一个类型签名系统,和你现在的差不多,只不过这个装饰器可以接收参数。例如:
@signature(int, str, int)
func(x, y, z):
...
这样的装饰器其实很容易就能构建出来。像这样(编辑 -- 可以工作!):
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()])
return fn(*new_args, **new_kwargs)
return wrapped
return decorator
就这样,你现在可以给函数添加类型签名了!
@signature(int, int)
def foo(x, y):
print type(x)
print type(y)
print x+y
>>> foo('3','4')
<type: 'int'>
<type: 'int'>
7
基本上,这就是@utdemir方法的一个类型明确的版本。
9
如果你信任这个字符串的来源,可以直接用普通的eval来处理它:
>>> eval("3.2", {}, {})
3.2
>>> eval("True", {}, {})
True
但如果你不信任这个来源,可以使用ast模块里的literal_eval。
>>> ast.literal_eval("'hi'")
'hi'
>>> ast.literal_eval("(5, 3, ['a', 'b'])")
(5, 3, ['a', 'b'])
补充:正如Ned Batchelder的评论所说,它不接受没有引号的字符串,所以我加了一个解决办法,还有一个关于带关键字参数的自动转换装饰器的例子。
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)]
21
如果你想要自动转换数值:
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
你可以调整 boolify
这个函数,让它接受其他的布尔值(也就是真或假的值),如果你愿意的话。