关于julia内省:julia introspection – 获取传递给函数的变量名称

julia introspection - get name of variable passed to function

在朱莉娅,有没有办法获得传递给函数的名称?

1
2
3
4
5
x = 10
function myfunc(a)
# do something here
end
assert(myfunc(x) =="x")

我是否需要使用宏或是否有提供内省的本机方法?


您可以使用宏获取变量名称:

1
2
3
4
5
6
7
8
julia> macro mymacro(arg)
           string(arg)
       end

julia> @mymacro(x)
"x"

julia> @assert(@mymacro(x) =="x")

但正如其他人所说,我不确定你为什么需要这样做。

宏在编译期间在AST(代码树)上运行,x作为符号:x传递到宏中。您可以将符号转换为字符串,反之亦然。宏用代码替换代码,因此只需拉出@mymacro(x)并替换为string(:x)


另一种方法是使用函数的vinfo。这是一个例子:

1
2
3
4
5
6
7
8
function test(argx::Int64)
    vinfo = code_lowered(test,(Int64,))
    string(vinfo[1].args[1][1])
end
test (generic function with 1 method)

julia> test(10)
"argx"

以上内容取决于知道函数的签名,但如果它是在函数本身内编码的话,这是一个非问题(否则可能需要一些宏魔法)。


好吧,与我自相矛盾:从技术上讲,这是可能的,在一个(相当有限的)条件下非常黑客的方式:函数名称必须只有一个方法签名。这个想法非常类似于Python的这些问题的答案。在演示之前,我必须强调这些是内部编译器细节,并且可能会发生变化。简述:

1
2
3
4
5
6
7
8
9
julia> function foo(x)
           bt = backtrace()
           fobj = eval(current_module(), symbol(Profile.lookup(bt[3]).func))
           Base.arg_decl_parts(fobj.env.defs)[2][1][1]
       end
foo (generic function with 1 method)

julia> foo(1)
"x"

让我再次强调,这是一个坏主意,不应该用于任何事情! (好吧,除了回溯显示)。这基本上是"愚蠢的编译器技巧",但我正在展示它,因为它可以与这些对象一起玩,并且解释确实导致对@ejang澄清评论的更有用的答案。

说明:

  • bt = backtrace()从当前位置生成...回溯...。 bt是一个指针数组,其中每个指针是当前调用堆栈中帧的地址。
  • Profile.lookup(bt[3])返回带有函数名称的LineInfo对象(以及关于每个帧的其他几个细节)。请注意,bt[1]bt[2]属于回溯生成函数本身,因此我们需要进一步向上移动堆栈以获取调用者。
  • Profile.lookup(...).func返回函数名称(符号:foo)
  • eval(current_module(), Profile.lookup(...))返回与current_module()中名称:foo关联的函数对象。如果我们修改function foo的定义以返回fobj,那么请注意REPL中与foo对象的等价:

    1
    2
    3
    4
    5
    6
    7
    8
    julia> function foo(x)
               bt = backtrace()
               fobj = eval(current_module(), symbol(Profile.lookup(bt[3]).func))
           end
    foo (generic function with 1 method)

    julia> foo(1) == foo
    true
  • fobj.env.defsMethodTable返回foo / fobj的第一个Method条目

  • Base.decl_arg_parts是辅助函数(在methodshow.jl中定义),它从给定的Method中提取参数信息。
  • 索引的其余部分深入到参数的名称。

关于函数只有一个方法签名的限制,原因是MethodTable中将列出多个签名(参见defs.next)。据我所知,目前没有公开的接口来获取与给定帧地址相关的特定方法。 (作为高级读者的练习:执行此操作的一种方法是修改jl_getFunctionInfo中的地址查找功能,以返回受损的函数名称,然后可以将其与特定的方法调用重新关联;但是,我不要以为我们当前存储了来自错位名称 - > Method的反向映射。

还要注意(1)回溯很慢(2)Julia中没有"函数本地"eval的概念,所以即使有一个变量名,我相信实际上不可能实际访问变量(和编译器)可能完全忽略局部变量,未使用或以其他方式,将它们放入寄存器等)

至于注释中提到的IDE风格的内省使用:foo.env.defs如上所示是"对象内省"的起点。从调试方面,Gallium.jl可以检查给定帧中的DWARF局部变量信息。最后,JuliaParser.jl是Julia解析器的纯Julia实现,它在几个IDE中被主动用于内省高级代码块。