Python:如何在set中使用ast.literal_eval()?

时间:2016-12-27 16:11:00

标签: python abstract-syntax-tree

集合不可哈及,因为它们是可变的。但是有没有办法在{1,2,{3,4}}上使用literal_eval?我只是想知道外部结构是一个集合,我不关心内部类型,但是集合内的集合是可能的输入。

更新:

输入从文件中读取为字符串。

2 个答案:

答案 0 :(得分:1)

你可以破解ast.literal_eval使其在看到一个集合时返回一个冻结集。以下是该怎么做:

  • 搜索Python安装库
  • 的位置
  • 它包含包含函数ast.py
  • 的文件literal_eval
  • 在您自己的模块中复制该功能(使用其他名称)并将其更改为从ast模块中导入所有相关名称
  • 在处理Set的行中,将set的生成替换为frozenset

然后,您可以使用它来安全地解析包含集合的文字集。对于我的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 :(得分:0)

对问题1的回答

  

集合内的集合是否可以输入?

您可以使用frozenset创建嵌套集对象:

  

返回一个新的set或frozenset对象,其元素取自iterable。集合的元素必须是可以清除的。要表示集合集,内部集合必须是冻结集合对象。如果未指定iterable,则返回一个新的空集。

例如:

>>> frozen_set = frozenset({3, 4})

>>> my_set = {1, 2, frozen_set}
>>> my_set
{frozenset({3, 4}), 2, 1}

对问题2的回答

  

有没有办法在{1,2,{3,4}}上使用literal_eval?

不,因为ast.literal_eval可迭代没有frozenset,所以没办法。 2008年由{em> Raymond Hettinger 为frozenset字面值proposal。 Guido甚至宣布了他的agreement但后来改变了主意。查看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?