11

I came across this code and it works, but I am not entirely sure about when to use ast and whether there are performance issues when this is used instead of getting the string value from input() and converting it to int.

import ast

cyper_key = ast.literal_eval(input("Enter the key (a value between 0 and 25) : "))

# this get the user input as an int to the variable cyper_key

I read the docs I understand what it does.

This can be used for safely evaluating strings containing Python values from untrusted sources without the need to parse the values oneself. It is not capable of evaluating arbitrarily complex expressions, for example involving operators or indexing.

I am looking for an explanation on above bold points.

user
  • 5,370
  • 8
  • 47
  • 75

4 Answers4

16

When to use it.

ast.literal_eval(input()) would be useful if you expected a list (or something similar) by the user. For example '[1,2]' would be converted to [1,2].

If the user is supposed to provide a number ast.literal_eval(input()) can be replaced with float(input()), or int(input()) if an integer is expected.


Performance

Note that premature [micro-]optimization is the root of all evil. But since you asked:

To test the speed of ast.literal_eval(input()) and float(input() you can use timeit.

Timing will vary based on the input given by the user.

Ints and floats are valid input, while anything else would be invalid. Giving 50% ints, 40% floats and 10% random as input, float(input()) is x12 faster.

With 10%, 10%, 80% and float(input()) is x6 faster.

import timeit as tt

lst_size = 10**5

# Set the percentages of input tried by user.
percentages = {'ints': .10,
               'floats': .10,
               'strings': .80}
assert 1 - sum(percentages.values()) < 0.00000001

ints_floats_strings = {k: int(v*lst_size) for k, v in percentages.items()}

setup = """
import ast

def f(x):
    try:
        float(x)
    except:
        pass

def g(x):
    try:
        ast.literal_eval(x)
    except:
        pass

l = [str(i) for i in range({ints})]
l += [str(float(i)) for i in range({floats})]
l += [']9' for _ in range({strings}//2)] + ['a' for _ in range({strings}//2)]
""".format(**ints_floats_strings)

stmt1 = """
for i in l:
    f(i)
"""

stmt2 = """
for i in l:
    g(i)
"""


reps = 10**1
t1 = tt.timeit(stmt1, setup, number=reps)
t2 = tt.timeit(stmt2, setup, number=reps)

print(t1)
print(t2)

print(t2/t1)
user
  • 5,370
  • 8
  • 47
  • 75
2
ast -> Abstract Syntax Trees 

ast.literal_eval raises an exception if the input isn't a valid Python datatype, so the code won't be executed if it's not. This link AST is useful for you to understand ast.

Sakib Ahammed
  • 2,452
  • 2
  • 25
  • 29
1

If it's going to be used as an int, then just use:

cypher_key = int(input("Enter the key (a value between 0 and 25) : "))

Only use that if you expect the user to be entering 10e7 or something. If you want to handle different bases, you can use int(input(...), 0) to automatically divine the base. If it really is an integer value between 0 and 25, there's no reason to use ast.

Eli Rose
  • 6,788
  • 8
  • 35
  • 55
  • That's not the problem, the OP addresses this in his first paragraph. – A.J. Uppal Apr 10 '15 at 03:51
  • Yeah, you're right, more info was needed. If the legal input is an integer between 0 and 25, though, I can't think of any situation where it would be better to use `ast` when `int` will be 1) more readable and 2) give better errors. – Eli Rose Apr 10 '15 at 03:56
0

Running this in a shell, I get no differences when I give correct input:

>>> cyper_key = ast.literal_eval(input("Enter the key (a value between 0 and 25) : "))
Enter the key (a value between 0 and 25) : 5
>>> cyper_key
5

However, when you give a string or something that cannot be converted, the error can be confusing and/or misleading:

>>>  cyper_key = ast.literal_eval(input("Enter the key (a value between 0 and 25) : "))
Enter the key (a value between 0 and 25) :  foo
Traceback (most recent call last):
  File "python", line 3, in <module>
ValueError: malformed node or string: <_ast.Name object at 0x136c968>

However, this can be useful if you don't want to cast either float or int to your input, which may lead to ValueErrors for your int or floating points for your float.

Thus, I see no necessary use in using ast to parse your input, but it can work as an alternate.

A.J. Uppal
  • 19,117
  • 6
  • 45
  • 76