Python: 如何对集合中的集合使用ast.literal_eval()?
Python: How to use ast.literal_eval() for sets in set?
集合不可散列,因为它们是可变的。但是有没有办法在 {1, 2, {3, 4}} 上仍然使用 literal_eval?我只想知道外部结构是一个集合,我不关心内部类型,但集合中的集合是可能的输入。
更新:
输入作为字符串从文件中读取。
问题 1 的答案:
Are sets within sets are possible input?
您可以使用 frozenset
创建嵌套集合对象:
Return a new set or frozenset object whose elements are taken from iterable. The elements of a set must be hashable. To represent sets of sets, the inner sets must be frozenset objects. If iterable is not specified, a new empty set is returned.
例如:
>>> frozen_set = frozenset({3, 4})
>>> my_set = {1, 2, frozen_set}
>>> my_set
{frozenset({3, 4}), 2, 1}
问题 2 的答案:
Is there a way to still use literal_eval on {1, 2, {3, 4}}?
不,没有办法,因为没有 ast.literal_eval
for frozenset
可迭代。有一个 proposal in 2008 by Raymond Hettinger for frozenset
literal. Guido even pronounced his agreement but then changed his mind. Check this mail 以获得更多见解。
但您可以使用 eval
代替:
>>> my_str = '{frozenset({3, 4}), 2, 1}'
>>> eval(my_str)
{1, 2, frozenset({3, 4})}
在使用eval
之前,请同时阅读:Why should exec()
and eval()
be avoided?
您可以破解 ast.literal_eval
使其 return 在看到集合时成为冻结集合。方法如下:
- 搜索 Python 安装的库所在的位置
- 它包含包含函数
literal_eval
的文件 ast.py
- 在您自己的模块中复制该函数(使用不同的名称)并将其更改为从
ast
模块 导入所有相关名称
- 在处理
Set
的行中,用frozenset
替换set
的生成
然后您可以使用它来安全地解析包含集合的文字集。对于我的 Python 3.5,我使用了:
def frozen_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.
SPECIAL: This version uses frozensets instead of sets
"""
# SPECIAL: import names from ast module
from ast import parse, Expression, Str, Bytes, Num, Tuple, List, Set, Dict
from ast import NameConstant, UnaryOp, UAdd, USub, BinOp, Add, Sub
# END SPECIAL
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
def _convert(node):
if 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):
#SPECIAL: returns a frozenset
return frozenset(map(_convert, node.elts))
# END SPECIAL
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)) and \
isinstance(node.operand, (Num, UnaryOp, BinOp)):
operand = _convert(node.operand)
if isinstance(node.op, UAdd):
return + operand
else:
return - operand
elif isinstance(node, BinOp) and \
isinstance(node.op, (Add, Sub)) and \
isinstance(node.right, (Num, UnaryOp, BinOp)) and \
isinstance(node.left, (Num, UnaryOp, BinOp)):
left = _convert(node.left)
right = _convert(node.right)
if isinstance(node.op, Add):
return left + right
else:
return left - right
raise ValueError('malformed node or string: ' + repr(node))
return _convert(node_or_string)
我可以使用:
>>> s = '{ 1, 2, {3, 4}}'
>>> frozen_literal_eval(s)
frozenset({1, 2, frozenset({3, 4})})
集合不可散列,因为它们是可变的。但是有没有办法在 {1, 2, {3, 4}} 上仍然使用 literal_eval?我只想知道外部结构是一个集合,我不关心内部类型,但集合中的集合是可能的输入。
更新:
输入作为字符串从文件中读取。
问题 1 的答案:
Are sets within sets are possible input?
您可以使用 frozenset
创建嵌套集合对象:
Return a new set or frozenset object whose elements are taken from iterable. The elements of a set must be hashable. To represent sets of sets, the inner sets must be frozenset objects. If iterable is not specified, a new empty set is returned.
例如:
>>> frozen_set = frozenset({3, 4})
>>> my_set = {1, 2, frozen_set}
>>> my_set
{frozenset({3, 4}), 2, 1}
问题 2 的答案:
Is there a way to still use literal_eval on {1, 2, {3, 4}}?
不,没有办法,因为没有 ast.literal_eval
for frozenset
可迭代。有一个 proposal in 2008 by Raymond Hettinger for frozenset
literal. Guido even pronounced his agreement but then changed his mind. Check this mail 以获得更多见解。
但您可以使用 eval
代替:
>>> my_str = '{frozenset({3, 4}), 2, 1}'
>>> eval(my_str)
{1, 2, frozenset({3, 4})}
在使用eval
之前,请同时阅读:Why should exec()
and eval()
be avoided?
您可以破解 ast.literal_eval
使其 return 在看到集合时成为冻结集合。方法如下:
- 搜索 Python 安装的库所在的位置
- 它包含包含函数
literal_eval
的文件 - 在您自己的模块中复制该函数(使用不同的名称)并将其更改为从
ast
模块 导入所有相关名称
- 在处理
Set
的行中,用frozenset
替换
ast.py
set
的生成
然后您可以使用它来安全地解析包含集合的文字集。对于我的 Python 3.5,我使用了:
def frozen_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.
SPECIAL: This version uses frozensets instead of sets
"""
# SPECIAL: import names from ast module
from ast import parse, Expression, Str, Bytes, Num, Tuple, List, Set, Dict
from ast import NameConstant, UnaryOp, UAdd, USub, BinOp, Add, Sub
# END SPECIAL
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
def _convert(node):
if 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):
#SPECIAL: returns a frozenset
return frozenset(map(_convert, node.elts))
# END SPECIAL
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)) and \
isinstance(node.operand, (Num, UnaryOp, BinOp)):
operand = _convert(node.operand)
if isinstance(node.op, UAdd):
return + operand
else:
return - operand
elif isinstance(node, BinOp) and \
isinstance(node.op, (Add, Sub)) and \
isinstance(node.right, (Num, UnaryOp, BinOp)) and \
isinstance(node.left, (Num, UnaryOp, BinOp)):
left = _convert(node.left)
right = _convert(node.right)
if isinstance(node.op, Add):
return left + right
else:
return left - right
raise ValueError('malformed node or string: ' + repr(node))
return _convert(node_or_string)
我可以使用:
>>> s = '{ 1, 2, {3, 4}}'
>>> frozen_literal_eval(s)
frozenset({1, 2, frozenset({3, 4})})