Python的ast.literal_eval()在Julia中的等效物是什么?

11

Julia 有没有类似于 Python 的 literal_eval 函数?Python 中这个函数由包 ast (Abstract Syntax Tree) 提供。

literal_eval 是用来评估 Python 字面结构的函数,包括字符串、字节、数字、元组、列表、字典、集合、布尔值和 None。当需要安全地对来自不可信源的字符串进行计算时,可以使用该函数,无需手动解析这些值。但该函数不能评估任意复杂的表达式,例如包含运算符或索引等表达式。


2
我已经添加了更多信息,这样人们就可以知道你想要什么,而不必了解Python。 - phipsgabler
相关链接:https://stackoverflow.com/q/30962477/179081 - Frames Catherine White
2个回答

17
没有相应的等价物,但你可以通过解析代码并在评估之前递归地确保结果表达式中只有某些语法形式来相对容易地编写一个。然而,与 Python 不同的是,许多基本类型及其语法和行为是内置且不可改变的,Julia 的“内置”类型只是在系统启动之前定义的用户定义类型。例如,让我们探讨一下当您使用矢量文字语法时会发生什么:
julia> :([1,2,3]) |> dump
Expr
  head: Symbol vect
  args: Array{Any}((3,))
    1: Int64 1
    2: Int64 2
    3: Int64 3
  typ: Any

julia> f() = [1,2,3]
f (generic function with 2 methods)

julia> @code_lowered f()
CodeInfo(:(begin
        nothing
        return (Base.vect)(1, 2, 3)
    end))

julia> methods(Base.vect)
# 3 methods for generic function "vect":
vect() in Base at array.jl:63
vect(X::T...) where T in Base at array.jl:64
vect(X...) in Base at array.jl:67

因此,[1,2,3]只是一个语法形式,它被转换为对Base.vect函数的调用,即Base.vect(1,2,3)。现在,我们可能会在未来使一些函数“封闭”,以便不能添加任何子方法或以任何方式重写其行为,但目前可以完全修改Base.vect对某些参数集的行为:

julia> function Base.vect(a::Int, b::Int, c::Int)
           warn("SURPRISE!")
           return invoke(Base.vect, Tuple{Any,Any,Any}, a, b, c)
       end

julia> [1,2,3]
WARNING: SURPRISE!
3-element Array{Int64,1}:
 1
 2
 3

由于在Julia中,数组字面量是可以重载的,因此它并不是一个纯粹的字面语法。当然,我不建议做我刚才所做的事情——“SURPRISE!”不是你想在程序中看到的东西——但这是可能的,因此该语法在这个问题的意义上并不是“安全”的。一些其他在Python或JavaScript(或大多数脚本语言)中用字面量表示的结构,在Julia中明确为函数调用,例如构造字典:
julia> Dict(:foo => 1, :bar => 2, :baz => 42)
Dict{Symbol,Int64} with 3 entries:
  :baz => 42
  :bar => 2
  :foo => 1

这只是对 Dict 类型进行函数调用,使用了三个键值对作为参数,根本不是字面量语法。而 a => b 的形式本身也只是对 => 操作符进行函数调用的特殊语法,它是 Pair 类型的别名。
julia> dump(:(a => b))
Expr
  head: Symbol call
  args: Array{Any}((3,))
    1: Symbol =>
    2: Symbol a
    3: Symbol b
  typ: Any

julia> :foo => 1.23
:foo=>1.23

julia> =>
Pair

julia> Pair(:foo, 1.23)
:foo=>1.23

整数字面量怎么样?它们肯定是安全的!嗯,是和不是。小的整数字面量目前是安全的,因为它们在解析器中直接转换为 Int 值,没有任何可重载的入口点(但是这可能会在将来改变,允许用户代码选择不同的整数字面量行为)。然而,足够大的整数字面量会被降级为宏调用,例如:
julia> :(18446744073709551616)
:(@int128_str "18446744073709551616")

如果整数字面值过大,超出了Int64类型的范围,则会将其降级为一个宏调用,并使用包含整数数字的字符串参数,使得宏能够解析该字符串并返回一个适当的整数对象——在这种情况下是一个Int128值,以便将其拼接到抽象语法树中。但您可以为这些宏定义新的行为:
julia> macro int128_str(s)
           warn("BIG SUPRISE!")
           9999999999999999
       end    

julia> 18446744073709551616
WARNING: BIG SUPRISE!
9999999999999999

基本上,Julia不存在有意义的“安全文字子集”。哲学上,Julia与Python非常不同:Julia没有内置一组带有特殊功能且用户定义的类型无法访问的固定类型,Julia包含的语言机制足够强大,可以从语言本身构建语言——这个过程称为“引导”(bootstrapping)。这些强大的语言机制对Julia程序员和Julia的程序员来说都是一样的。这就是Julia的灵活性和强大性的来源。但伴随着强大的力量而来的是伟大的责任等等……所以除非你有一个非常好的理由,否则不要像我在这个答案中做的那样做任何事情:)
回到您最初的问题,创建使用Julia语法的安全文字对象构造解析器的最佳方法是实现对Julia子集的解析器,在其中给出文字它们“通常”的意义,这样就可以防止其被重载。这种安全语法子集可能包括数字文字,字符串文字,数组文字和Dict构造函数,例如。但实际上,更实用的方法可能是使用JSON语法,并使用Julia的JSON package进行解析。

2

2
问题在于它还会运行字符串中的任何代码。 - StefanKarpinski

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接