Smart type casting in python

北战南征 提交于 2020-01-05 14:22:16

问题


I am making api calls and receive a response back in json like so

result = {'foo': '123.456', 'bar': '23', 'donald': 'trump', 'time': '2016-04-07T05:31:49.532124Z'}

Although result is either a dictionary or a list, the contents are always strings. I want to cast these values to the appropriate type. (i.e. '123.456' to a float, '23' to an int, 'time' to a datetime.)

My code works but it seems like there should be a simpler/more efficient way to do this. Is there?

Here's my version

from dateutil.parser import parse

def is_float(x):
    try:
        float(x)
        return True
    except ValueError:
        return False

def is_int(x):
    try:
        a = float(x)
        b = int(a)
    except ValueError:
        return False
    else:
        return a == b

def is_datetime(x):
    try:
        parse(x)
        return True
    except ValueError:
        return False

def smartcast(x):
    if isinstance(x, dict):
        return { k:smartcast(v) for k, v in x.items() }
    elif isinstance(x, list):
        return map(smartcast, x)
    elif is_int(x):
        return int(x)
    elif is_float(x):
        return float(x)
    elif is_datetime(x):
        return parse(x)
    else:
        return x

Edit: If possible I'd like to avoid using try and except. I'd like to implement this into an asynchronous twisted program and I think try and except block so it just makes the program synchronous. I'm new to twisted so I'm not sure if that is true.


As per Francesco's solution, here's the updated code.

from dateutil.parser import parse

def smartcast(x):
    if isinstance(x, dict):
        return { k:smartcast(v) for k, v in x.items() }
    elif isinstance(x, list):
        return map(smartcast, x)
    else:
        for t in [int, float, parse]:
            try:
                return t(x)
            except ValueError:
                continue
    return x

回答1:


And what is the appropriate type? I am asking this to underline that sometimes also for humans it is not clear what the type is: are you sure that "42" is always an int or sometimes it has to be considered just as a string?

Anyway, as you did, you can build your solution with your rules that makes sense in your context.

In your case, you can simplify your code with a simple loop

from dateutil.parser import parse

tests = [int, float, parse]

def smartcast(value):
     for test in tests:
         try:
             return test(value)
         except ValueError:
             continue
     # No match
     return value



回答2:


what about using ast.literal_eval()?

https://docs.python.org/2/library/ast.html#ast.literal_eval

voting up Francesco's answer. You need an exception raised.



来源:https://stackoverflow.com/questions/36467619/smart-type-casting-in-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!