简体   繁体   中英

Smart type casting in python

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. (ie '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

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM