[New-bugs-announce] [issue31753] Unnecessary closure in ast.literal_eval
Aaron Hall
report at bugs.python.org
Tue Oct 10 18:02:07 EDT 2017
New submission from Aaron Hall <aaronchall at yahoo.com>:
Removing the closure seems to make the function about 10% faster.
Original source code at: https://github.com/python/cpython/blob/3.6/Lib/ast.py#L40
Empirical evidence: astle.py
import timeit
from ast import literal_eval as orig_literal_eval
from ast import *
def new_literal_eval(node_or_string):
"""
Safely evaluate an expression node or a string containing a Python
expression. The string or node provided may only consist of the following
Python literal structures: strings, bytes, numbers, tuples, lists, dicts,
sets, booleans, and None.
"""
if isinstance(node_or_string, str):
node_or_string = parse(node_or_string, mode='eval')
if isinstance(node_or_string, Expression):
node_or_string = node_or_string.body
node = node_or_string
if isinstance(node, Constant):
return node.value
elif isinstance(node, (Str, Bytes)):
return node.s
elif isinstance(node, Num):
return node.n
elif isinstance(node, Tuple):
return tuple(map(_convert, node.elts))
elif isinstance(node, List):
return list(map(_convert, node.elts))
elif isinstance(node, Set):
return set(map(_convert, node.elts))
elif isinstance(node, Dict):
return dict((_convert(k), _convert(v)) for k, v
in zip(node.keys, node.values))
elif isinstance(node, NameConstant):
return node.value
elif isinstance(node, UnaryOp) and isinstance(node.op, (UAdd, USub)):
operand = _convert(node.operand)
if isinstance(operand, _NUM_TYPES):
if isinstance(node.op, UAdd):
return + operand
else:
return - operand
elif isinstance(node, BinOp) and isinstance(node.op, (Add, Sub)):
left = _convert(node.left)
right = _convert(node.right)
if isinstance(left, _NUM_TYPES) and isinstance(right, _NUM_TYPES):
if isinstance(node.op, Add):
return left + right
else:
return left - right
raise ValueError('malformed node or string: ' + repr(node))
def main():
print('orig first, then new')
print("'1.01'")
print(min(timeit.repeat(lambda: orig_literal_eval('1.01'))))
print(min(timeit.repeat(lambda: new_literal_eval('1.01'))))
print("""'"1.01"'""")
print(min(timeit.repeat(lambda: orig_literal_eval('"1.01"'))))
print(min(timeit.repeat(lambda: new_literal_eval('"1.01"'))))
print("'1'")
print(min(timeit.repeat(lambda: orig_literal_eval('1'))))
print(min(timeit.repeat(lambda: new_literal_eval('1'))))
if __name__ == '__main__':
main()
Shell:
$ python -m astle
orig first, then new
'1.01'
3.518230145502848
3.274753015923377
'"1.01"'
3.189016693752965
2.906869704238048
'1'
3.40557457956146
3.157061471625788
----------
components: Library (Lib)
messages: 304089
nosy: Aaron Hall
priority: normal
severity: normal
status: open
title: Unnecessary closure in ast.literal_eval
type: performance
_______________________________________
Python tracker <report at bugs.python.org>
<https://bugs.python.org/issue31753>
_______________________________________
More information about the New-bugs-announce
mailing list