关于python:查找标识符的等效字符串(如源代码所示)?

Finding an identifier's string equivalent (as expressed in source code)?

注意:这个问题的措辞看似简单,但可能需要额外的智力努力来回答。尝试采用一种方法,比如如何在Python中获取对象的名称?不是有效答案(您可能需要先阅读)。根据定义,通过对象的这种方法是不明确的,因为任何对象都可以被多个名称引用,甚至没有名称引用。建议的方法要求回到解析的步骤,对象还没有任何存在。

背景:我正在探索如何在Python中创建允许我们消除一些冗余的结构,例如,在进行单元测试时:

1
2
y = 5
print("%s: %s" % ('y', y))

最好是写:

1
2
y = 5    
print(info(y)) =>"Value of 'y' is 5"

注意:这可以通过另一种方式解决,方法是传递一个字符串,然后将其转换为一个ID,例如,使用globals()['y']之类的东西将其转换为print(info('y')。但是这里我们想从ID转到字符串,因为有些情况下这样做比较简单,例如当一个测试例程被传递一个要检查的变量列表时。

问题:在源代码中给定一个名为x的标识符,是否可以编写一个返回x对应字符串的symbol函数?

在语义上,如果我写

1
x = [1, 2, 3, 4] # whatever object

从源代码中可以明显看出,x是字符串"x"。

换言之,是否可以(在标准python中)编写一个符号函数(只需输入一个名称),给定x,在函数被调用的确切位置将其视为符号,并返回字符串"x"?

1
2
3
4
x = [1, 2, 3, 4] # whatever object
y = x
symbol(x) = 'x'
symbol(y) = 'y'

答案是明确的:在调用函数时,x和y被用作源代码中的符号,因此符号的结果必须是与符号等效的字符串("x"和"y")。

我意识到这可能是一个很好的二级反省工作(在源代码(ast?)中找到位置)。调用函数的位置,然后进行推理)。但是,是否可以像现在这样使用Python?

因此,如果建议的符号函数有效,那么应该能够编写(通常用于单元测试):

1
2
3
4
   x = 5
   y = x
   print("The value of %s is %s", symbol(x), x) => 5
   print("The value of %s is %s", symbol(y), y) => 5

那么,info函数可以写成:

1
2
def info(y):
    return("%s: %s" % (symbol(y), y))

注1:出于通用性的考虑,解决方案应该在标准的Python中工作,但是对于其他实现的答案也是可以接受的。

注2:如果您觉得"符号"不是这个函数或概念的正确名称,欢迎您指出这一点。

注3:这是一个精确的问题:考虑到代码中使用了符号Y,编写一个符号函数,它给出符号(Y)='Y',而不考虑符号指向的对象(类、对象等)。


这可以通过读取调用symbol函数的源代码来实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import inspect
import re

def symbol(arg):
    frame = inspect.currentframe().f_back
    frameinfo = inspect.getouterframes(frame)[0]
    calling_code = frameinfo[4][frameinfo[5]].strip()
    del frame

    # this adds support for multiple calls in the same line
    matches = re.findall(r'\bsymbol\((\w+)\)', calling_code)
    match = matches[symbol.call_count]
    symbol.call_count += 1
    if symbol.call_count == len(matches):
        symbol.call_count = 0
    return match

symbol.call_count = 0

1
2
x = 3
print(symbol(x)) # output: x

注意事项:

  • 不能在交互式会话或任何其他无法读取源代码的情况下工作
  • 可能被串绊倒:

    1
    2
    fake ="symbol(fake)"; print(symbol(5))
    # output: fake


不确定我的问题是否正确,但下面是一个在:globals()中搜索符号的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import sys

x = [1, 2, 33]


def dummy():
    pass


def symbol(obj):
    globals_dict = globals()
    for name in globals_dict:
        if obj is globals_dict[name]:
            return name


def main():
    for obj in [sys, x, dummy, symbol, main]:
        obj_name = symbol(obj)
        print(obj_name, type(obj_name))


if __name__ =="__main__":
    main()

输出:

1
2
3
4
5
6
E:\Work\Dev\StackOverflow\q47091183>"E:\WinOBT\1.0.0.0\OPSWpython\2.7.10__00\x86\python.exe" a.py
('sys', <type 'str'>)
('x', <type 'str'>)
('dummy', <type 'str'>)
('symbol', <type 'str'>)
('main', <type 'str'>)