在 Python 中自动类型转换参数

发布于 2024-11-29 00:00:57 字数 2489 浏览 0 评论 0原文

背景:
我主要从管道中的命令行运行 python 脚本,因此我的参数始终是需要类型转换为适当类型的字符串。我每天都会制作很多小脚本,并且为每个脚本对每个参数进行类型转换会花费比应有的时间更多的时间。

问题:
是否有一种规范的方法可以自动为函数键入强制类型转换参数?

我的方式:
如果没有更好的方法,我开发了一个装饰器来做我想做的事情。装饰器是下面的 autocast fxn。示例中,修饰后的 fxn 为 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')

Background:
I mostly run python scripts from the command line in pipelines and so my arguments are always strings that need to be type casted to the appropriate type. I make a lot of little scripts each day and type casting each parameter for every script takes more time than it should.

Question:
Is there a canonical way to automatically type cast parameters for a function?

My Way:
I've developed a decorator to do what I want if there isn't a better way. The decorator is the autocast fxn below. The decorated fxn is fxn2 in the example. Note that at the end of the code block I passed 1 and 2 as strings and if you run the script it will automatically add them. Is this a good way to do this?

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')      

EDIT: For anyone that comes here and wants the updated and concise working version go here:

https://github.com/sequenceGeek/cgAutoCast

And here is also quick working version based on above:

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')

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

撧情箌佬 2024-12-06 00:00:57

如果您想自动转换值:

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 以接受其他布尔值。

If you want to auto-convert values:

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

You can adjust boolify to accept other boolean values if you like.

快乐很简单 2024-12-06 00:00:57

如果您信任源,您可以只使用纯 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 的评论,它不接受非引号字符串,所以我添加了一个解决方法,也是一个关于带有关键字参数的 autocaste 装饰器的示例。

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)]

You could just use plain eval to input string if you trust the source:

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

But if you don't trust the source, you could use literal_eval from ast module.

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

Edit:
As Ned Batchelder's comment, it won't accept non-quoted strings, so I added a workaround, also an example about autocaste decorator with keyword arguments.

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)]
|煩躁 2024-12-06 00:00:57

我想你可以用函数装饰器创建一个类型签名系统,就像你所拥有的那样,只有一个需要参数的系统。例如:

@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 方法的类型显式版本。

I'd imagine you can make a type signature system with a function decorator, much like you have, only one that takes arguments. For example:

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

Such a decorator can be built rather easily. Something like this (EDIT -- works!):

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

And just like that, you can now imbue functions with type signatures!

@signature(int, int)
def foo(x, y):
    print type(x)
    print type(y)
    print x+y

>>> foo('3','4')
<type: 'int'>
<type: 'int'>
7

Basically, this is an type-explicit version of @utdemir's method.

心的位置 2024-12-06 00:00:57

如果您要从命令行解析参数,则应使用 argparse 模块(如果您使用的是 Python 2.7)。

每个参数都可以有一个预期的类型,因此知道如何处理它应该相对简单。您甚至可以定义自己的类型。

...很多时候命令行字符串应该被解释为另一种类型,例如 float 或 int。 add_argument() 的 type 关键字参数允许执行任何必要的类型检查和类型转换。常见的内置类型和函数可以直接用作类型参数的值:

parser = argparse.ArgumentParser()
parser.add_argument('foo', type=int)
parser.add_argument('bar', type=file)
parser.parse_args('2 temp.txt'.split())
>>> Namespace(bar=<open file 'temp.txt', mode 'r' at 0x...>, foo=2)

If you're parsing arguments from the command line, you should use the argparse module (if you're using Python 2.7).

Each argument can have an expected type so knowing what to do with it should be relatively straightforward. You can even define your own types.

...quite often the command-line string should instead be interpreted as another type, like a float or int. The type keyword argument of add_argument() allows any necessary type-checking and type conversions to be performed. Common built-in types and functions can be used directly as the value of the type argument:

parser = argparse.ArgumentParser()
parser.add_argument('foo', type=int)
parser.add_argument('bar', type=file)
parser.parse_args('2 temp.txt'.split())
>>> Namespace(bar=<open file 'temp.txt', mode 'r' at 0x...>, foo=2)
指尖上的星空 2024-12-06 00:00:57

您的代码片段中有几个问题。

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

这将始终检查 True,因为您正在针对字符串 'True''False' 进行测试。

python 中没有自动类型强制转换。当您通过 *args**kwargs 接收时,您的参数可以是任何内容。首先将查找值列表(每个值可以是任何数据类型,原始数据类型和复杂数据类型),其次将查找映射(可能具有任何有效的映射)。因此,如果您编写一个装饰器,您最终将得到一个很好的错误检查列表。

通常,如果您希望以 str 形式发送,则在调用函数时,通过 (str) 将其类型转换为字符串并发送。

There are couple of problems in your snippet.

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

This would always check for True because you are testing against the strings 'True' and 'False'.

There is not an automatic coercion of types in python. Your arguments when you receive via *args and **kwargs can be anything. First will look for list of values (each of which can be any datatype, primitive and complex) and second will look for a mapping (with any valid mapping possible). So if you write a decorator, you will end up with a good list of error checks.

Normally, if you wish to send in str, just when the function is invoked, typecast it to string via (str) and send it.

新一帅帅 2024-12-06 00:00:57

我知道我在这场比赛中迟到了,但是评估怎么样?

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

或者(更安全):

from ast import literal_eval

def mycast(a):
  try:
    return literal_eval(a)
  except:
    return a

I know I arrived late at this game, but how about eval?

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

or alternatively (and more safely):

from ast import literal_eval

def mycast(a):
  try:
    return literal_eval(a)
  except:
    return a
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文