Automatically Type Cast Parameters In Python

后端 未结 6 2040
死守一世寂寞
死守一世寂寞 2020-12-30 04:37

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 app

相关标签:
6条回答
  • 2020-12-30 05:08

    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
    
    0 讨论(0)
  • 2020-12-30 05:11

    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.

    0 讨论(0)
  • 2020-12-30 05:12

    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.

    0 讨论(0)
  • 2020-12-30 05:13

    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.

    0 讨论(0)
  • 2020-12-30 05:23

    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)
    
    0 讨论(0)
  • 2020-12-30 05:26

    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)]
    
    0 讨论(0)
提交回复
热议问题