Julia 中 Python 的 ast.literal_eval() 等价物是什么?
问题描述
Julia 中有什么东西相当于 Python 的 literal_eval
包提供的ast
(抽象语法树)?
Is there anything in Julia which is equivalent to Python's literal_eval
provided by the package ast
(Abstract Syntax Tree)?
对其(literal_eval
)描述的总结:
这个函数只计算 Python 文字结构:字符串,字节、数字、元组、列表、字典、集合、布尔值和 None
,以及可用于安全地评估来自不受信任来源的字符串无需自己解析值.它没有能力评估任意复杂的表达式,例如涉及运算符或索引.
This function only evaluates Python literal structures: strings, bytes, numbers, tuples, lists, dicts, sets, booleans, and
None
, and can be used for safely evaluating strings 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.
解决方案
没有等价物,尽管您可以通过解析代码然后递归地确保在计算之前在结果表达式中只有某些语法形式来相当容易地编写一个它.然而,与许多基本类型及其语法和行为是内置且不可更改的 Python 不同,Julia 的内置"类型只是用户定义的类型,恰好在系统启动之前定义.让我们探讨一下,例如,当您使用向量字面量语法时会发生什么:
There is no equivalent, although you could potentially write one fairly easily by parsing code and then recursively ensuring that you only have certain syntactic forms in the resulting expression before evaluating it. However, unlike Python where a lot of basic types and their syntax and behavior are built in and unchangeable, Julia's "built in" types are just user-defined types that happen to be defined before the system starts up. Let's explore what happens, for example, when you use vector literal syntax:
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
的行为某些参数集是完全可能的:
So [1,2,3]
is just a syntactic form that is lowered as a call to the Base.vect
function, i.e. Base.vect(1,2,3)
. Now, we might in the future make it possible to "seal" some functions so that one can't add any submethods or overwrite their behavior in any way, but currently modifying the behavior of Base.vect
for some set of arguments is entirely possible:
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 中是可重载的,因此它并不是真正的纯字面量语法.当然,我不建议做我刚才做的事——惊喜!"不是您想在程序中间看到的东西——但这是可能的,因此从这个问题的意义上讲,语法不是安全的".在 Python 或 JavaScript(或大多数脚本语言)中用文字表示的其他一些构造在 Julia 中是显式的函数调用,例如构造字典:
Since an array literal is overloadable in Julia it's not really a purely literal syntax. Of course, I don't recommend doing what I just did – "SURPRISE!" is not something you want to see in the middle of your program – but it is possible and therefore the syntax is not "safe" in the sense of this question. Some other constructs which are expressed with literals in Python or JavaScript (or most scripting languages), are explicitly function calls in Julia, such as constructing dictionaries:
julia> Dict(:foo => 1, :bar => 2, :baz => 42)
Dict{Symbol,Int64} with 3 entries:
:baz => 42
:bar => 2
:foo => 1
这只是对 Dict
类型的函数调用,带有三个对对象参数,根本不是文字语法.a =>b
对语法本身 also 只是对 =>
运算符的函数调用的特殊语法,它是 Pair<的别名/code> 类型:
This is just a function call to the Dict
type with three pair object arguments, not a literal syntax at all. The a => b
pair syntax itself is also just a special syntax for a function call to the =>
operator, which is an alias for the Pair
type:
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
值,没有任何可重载的入口点(但将来可能会改变,允许用户代码选择不同的行为整数文字).然而,足够大的整数文字被降级为宏调用,例如:
What about integer literals? Surely those are safe! Well, yes and no. Small integer literals are currently safe, since they are converted in the parser directly to Int
values, without any overloadable entry points (that could change in the future, however, allowing user code to opt into different behaviors for integer literals). Large enough integer literals, however, are lowered to macro calls, for example:
julia> :(18446744073709551616)
:(@int128_str "18446744073709551616")
对于 Int64
类型来说太大的整数文字被降低为宏调用,其中包含整数位的字符串参数,允许宏解析字符串并返回适当的整数对象 - 在本例是一个 Int128
值——将被拼接到抽象语法树中.但是您可以为这些宏定义新的行为:
An integer literal that is too big for the Int64
type is lowered as macro call with a string argument containing the integer digits, allowing the macro to parse the string and return an appropriate integer object – in this case an Int128
value – to be spliced into the abstract syntax tree. But you can define new behaviors for these macros:
julia> macro int128_str(s)
warn("BIG SUPRISE!")
9999999999999999
end
julia> 18446744073709551616
WARNING: BIG SUPRISE!
9999999999999999
基本上,Julia 没有有意义的安全文字子集".从哲学上讲,Julia 与 Python 非常不同:Julia 不是构建一组具有用户定义类型无法访问的特殊功能的固定类型,而是在语言中包含足够强大的机制,使语言可以从自身内部构建——一个过程称为引导".这些强大的语言机制对 Julia 程序员和对 Julia 程序员一样可用.这就是 Julia 的大部分灵活性和力量的来源.但是强大的力量带来了巨大的责任和所有这些......所以除非你有一个真的充分的理由,否则不要真正做我在这个答案中所做的任何事情:)
Essentially, there is no meaningful "safe literal subset" of Julia. Philosophically, Julia is very different from Python: instead of building in a fixed set of types with special capabilities that are inaccessible to user-defined types, Julia includes powerful enough mechanisms in the language that the language can be built from within itself – a process known as "bootstrapping". These powerful language mechanisms are just as available to Julia programmers as they are to the programmers of Julia. This is where much of Julia's flexibility and power comes from. But with great power comes great responsibility and all that... so don't actually do any of the things I've done in this answer unless you have a really good reason :)
要回到您最初的问题,使用 Julia 语法为安全的文字对象构造创建解析器的最佳方法是为 Julia 的子集实现解析器,为文字提供它们的通常 以一种不能超载的方式表示.例如,这个安全的语法子集可以包括数字文字、字符串文字、数组文字和 Dict
构造函数.但只使用 JSON 语法并使用 Julia 的 JSON 包 解析它可能更实用.
To get back to your original problem, the best one could do to create a parser for safe literal object construction using Julia syntax would be to implement a parser for a subset of Julia, giving literals their usual meaning in a way that cannot be overloaded. This safe syntax subset could include numeric literals, string literals, array literals, and Dict
constructors, for example. But it would probably be more practical to just use JSON syntax and parse it using Julia's JSON package.
相关文章