关于introspection:如何在Python中获取对象的名称?

How can I get the name of an object in Python?

在python中有没有任何方法可以获取对象的名称?例如:

1
2
3
4
5
my_list = [x, y, z] # x, y, z have been previously defined

for bla in my_list:
    print"handling object", name(bla) # <--- what would go instead of `name`?
    # do something to bla

编辑:某些上下文:

实际上,我正在创建一个可以通过命令行指定的函数列表。

我有:

1
2
3
4
5
6
7
8
9
10
def fun1:
    pass
def fun2
    pass
def fun3:
    pass

fun_dict = {'fun1': fun1,
            'fun2': fun2,
            'fun3': fun3}

我从命令行中得到函数的名称,我想调用相关的函数:

1
2
3
func_name = parse_commandline()

fun_dict[func_name]()

我之所以想要函数名是因为我想创建fun_dict,而不需要两次写入函数名,因为这似乎是创建bug的好方法。我想做的是:

1
2
3
4
fun_list = [fun1, fun2, fun3] # and I'll add more as the need arises

fun_dict = {}
[fun_dict[name(t) = t for t in fun_list] # <-- this is where I need the name function

这样我只需要写一次函数名。


对象在python中不一定有名称,因此无法获取名称。在这些情况下,对象具有__name__属性并不罕见,因为它们确实有名称,但这不是标准python的一部分,而且大多数内置类型没有名称。

当您创建一个变量(如上面的x、y、z)时,这些名称就充当对象的"指针"或"引用"。对象本身不知道您为它使用的是什么名称,并且您不容易(如果有的话)获得对该对象的所有引用的名称。

更新:但是,函数确实有一个__name__(除非它们是lambda),所以在这种情况下,您可以这样做:

1
dict([(t.__name__, t) for t in fun_list])


这是不可能的,因为可能有多个变量具有相同的值,或者一个值可能没有变量,或者一个值可能只有偶然才具有与变量相同的值。

如果你真的想这样做,你可以用

1
2
3
4
5
def variable_for_value(value):
    for n,v in globals().items():
        if v == value:
            return n
    return None

但是,最好首先对名称进行迭代:

1
2
3
4
5
6
my_list = ["x","y","z"] # x, y, z have been previously defined

for name in my_list:
    print"handling variable", name
    bla = globals()[name]
    # do something to bla

nbsp;


如果您想获得在解释器中定义的函数或lambda或其他类似函数的对象的名称,可以使用dill中的dill.source.getname。它基本上是在寻找__name__方法,但在某些情况下,它知道如何找到名称的其他魔力…或对象的名称。我不想讨论如何为一个python对象找到一个真正的名称,不管这意味着什么。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
>>> from dill.source import getname
>>>
>>> def add(x,y):
...   return x+y
...
>>> squared = lambda x:x**2
>>>
>>> print getname(add)
'add'
>>> print getname(squared)
'squared'
>>>
>>> class Foo(object):
...   def bar(self, x):
...     return x*x+x
...
>>> f = Foo()
>>>
>>> print getname(f.bar)
'bar'
>>>
>>> woohoo = squared
>>> plus = add
>>> getname(woohoo)
'squared'
>>> getname(plus)
'add'

这一行程序适用于所有类型的对象,只要它们在globals()dict中,它们应该是:

1
2
3
def name_of_global_obj(xx):
    return [objname for objname, oid in globals().items()
            if id(oid)==id(xx)][0]

或者,等价地:

1
2
3
4
def name_of_global_obj(xx):
    for objname, oid in globals().items():
        if oid is xx:
            return objname


正如其他人提到的,这是一个非常棘手的问题。解决这一问题的办法不是"一刀切",甚至不是远程解决。困难(或放松)真的取决于你的处境。

我曾多次遇到过这个问题,但最近是在创建调试函数时遇到的。我希望函数将一些未知对象作为参数,并打印它们声明的名称和内容。当然,获取内容很容易,但是声明的名称是另一回事。

下面是我的一些想法。

返回函数名

确定函数名非常简单,因为它具有包含函数声明名称的名称属性。

1
2
3
4
5
6
7
name_of_function = lambda x : x.__name__

def name_of_function(arg):
    try:
        return arg.__name__
    except AttributeError:
        pass`

例如,如果创建函数def test_function(): passcopy_function = test_functionname_of_function(copy_function),它将返回test_函数。

返回第一个匹配的对象名

  • 检查对象是否具有名称属性,如果有,则返回该属性(仅声明函数)。请注意,您可以删除此测试,因为名称仍在globals()中。

  • 将arg的值与globals()中项目的值进行比较,并返回第一个匹配的名称。请注意,我正在筛选以"u"开头的名称。

  • 结果将由第一个匹配对象的名称组成,否则将为无。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def name_of_object(arg):
        # check __name__ attribute (functions)
        try:
            return arg.__name__
        except AttributeError:
            pass

        for name, value in globals().items():
            if value is arg and not name.startswith('_'):
                return name

    返回所有匹配的对象名称

    • 比较arg的值与globals()中项目的值,并将名称存储在列表中。请注意,我正在筛选以"u"开头的名称。

    结果将由一个列表(对于多个匹配项)和一个字符串(对于单个匹配项)组成,否则为无。当然,您应该根据需要调整这种行为。

    1
    2
    3
    def names_of_object(arg):
        results = [n for n, v in globals().items() if v is arg and not n.startswith('_')]
        return results[0] if len(results) is 1 else results if results else None

    这是另一种思考方法。假设有一个name()函数返回了其参数的名称。给出以下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    def f(a):
        return a

    b ="x"
    c = b
    d = f(c)

    e = [f(b), f(c), f(d)]

    name(e[2])应该返回什么?为什么?


    使用反向口述。

    1
    2
    3
    4
    5
    fun_dict = {'fun1': fun1,
                'fun2': fun2,
                'fun3': fun3}

    r_dict = dict(zip(fun_dict.values(), fun_dict.keys()))

    反向dict将把每个函数引用映射到您在fun-dict中给出的确切名称,这可能是您在定义函数时使用的名称,也可能不是。而且,这种技术推广到其他对象,包括整数。

    对于额外的乐趣和疯狂,您可以在同一个dict中存储正向和反向值。如果您正在将字符串映射到字符串,我不会这样做,但是如果您正在执行函数引用和字符串之类的操作,这并不太疯狂。


    And the reason I want to have the name of the function is because I want to create fun_dict without writing the names of the functions twice, since that seems like a good way to create bugs.

    为此,您有一个极好的getattr函数,它允许您通过已知的名称获取对象。所以你可以这样做,例如:

    funcs.py:

    1
    2
    def func1(): pass
    def func2(): pass

    main.py:

    1
    2
    3
    import funcs
    option = command_line_option()
    getattr(funcs, option)()


    注意,尽管如前所述,对象一般不知道也不知道哪些变量与它们绑定,但是使用def定义的函数在__name__属性中确实有名称(在def中使用的名称)。另外,如果函数是在同一个模块中定义的(如示例中所示),那么globals()将包含所需字典的超集。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def fun1:
      pass
    def fun2:
      pass
    def fun3:
      pass

    fun_dict = {}
    for f in [fun1, fun2, fun3]:
      fun_dict[f.__name__] = f

    python的名称映射到称为名称空间的hashmap中的对象。在任何时刻,一个名称总是引用一个对象,但是一个对象可以被任意数量的名称引用。给定一个名称,哈希映射查找该名称所引用的单个对象是非常有效的。但是,如果给定一个对象(如前所述,该对象可以由多个名称引用),则无法有效地查找引用该对象的名称。您需要做的是遍历名称空间中的所有名称,并分别检查每个名称,看看它是否映射到给定的对象。这很容易通过列表理解来实现:

    1
    [k for k,v in locals().items() if v is myobj]

    这将计算为包含当前映射到对象myobj的所有本地"变量"名称的字符串列表。

    1
    2
    3
    4
    5
    6
    7
    8
    >>> a = 1
    >>> this_is_also_a = a
    >>> this_is_a = a
    >>> b ="ligma"
    >>> c = [2,3, 534]
    >>> [k for k,v in locals().items() if v is a]

    ['a', 'this_is_also_a', 'this_is_a']

    当然,locals()可以被任何dict替换,您要搜索指向给定对象的名称。显然,对于非常大的名称空间,这种搜索可能会很慢,因为它们必须整体遍历。


    这是我的答案,我也在使用globals().items()。

    1
    2
    3
    4
    5
        def get_name_of_obj(obj, except_word =""):

            for name, item in globals().items():
                if item == obj and name != except_word:
                    return name

    我添加了except-word,因为我想过滤掉for循环中使用的一些单词。如果不添加它,for循环中的关键字可能会混淆此函数,有时像下面的"each_item"这样的关键字可能会显示在函数的结果中,这取决于您对循环所做的操作。

    如。

    1
    2
        for each_item in [objA, objB, objC]:
            get_name_of_obj(obj,"each_item")

    如。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
        >>> objA = [1, 2, 3]
        >>> objB = ('a', {'b':'thi is B'}, 'c')
        >>> for each_item in [objA, objB]:
        ...     get_name_of_obj(each_item)
        ...
        'objA'
        'objB'
        >>>
        >>>
        >>> for each_item in [objA, objB]:
        ...     get_name_of_obj(each_item)
        ...
        'objA'
        'objB'
        >>>
        >>>
        >>> objC = [{'a1':'a2'}]
        >>>
        >>> for item in [objA, objB, objC]:
        ...     get_name_of_obj(item)
        ...
        'objA'
        'item'  <<<<<<<<<< --------- this is no good
        'item'
        >>> for item in [objA, objB]:
        ...     get_name_of_obj(item)
        ...
        'objA'
        'item'     <<<<<<<<--------this is no good
        >>>
        >>> for item in [objA, objB, objC]:
        ...     get_name_of_obj(item,"item")
        ...
        'objA'
        'objB'  <<<<<<<<<<--------- now it's ok
        '
    objC'
        >>>

    希望这能有所帮助。


    定义一个class并添加unicode私有函数insert the classlike

    1
    2
    3
    4
    5
    6
    class example:
      def __init__(self, name):
        self.name = name

      def __unicode__(self):
        return self.name

    当然,您必须添加额外的变量self.name,这是对象的名称。


    我在想同一个问题的时候偶然看到了这一页。

    正如其他人所指出的,从函数中获取__name__属性以确定函数的名称已经足够简单了。对于那些没有健全的方法来确定__name__的对象,例如basestring实例、ints、long等基本/原始对象,这稍微有点棘手。

    长话短说,您可能可以使用inspect模块对它是哪个进行有根据的猜测,但您可能必须知道您在哪个帧中工作/向下遍历堆栈才能找到正确的帧。但我不想想象这会给处理eval/exec'ed代码带来多大的乐趣。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    % python2 whats_my_name_again.py
    needle => ''b''
    ['a', 'b']
    []
    needle => '<function foo at 0x289d08ec>'
    ['c']
    ['foo']
    needle => '<function bar at 0x289d0bfc>'
    ['f', 'bar']
    []
    needle => '<__main__.a_class instance at 0x289d3aac>'
    ['e', 'd']
    []
    needle => '<function bar at 0x289d0bfc>'
    ['f', 'bar']
    []
    %

    我的名字是什么?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    #!/usr/bin/env python

    import inspect

    class a_class:
        def __init__(self):
            pass

    def foo():
        def bar():
            pass

        a = 'b'
        b = 'b'
        c = foo
        d = a_class()
        e = d
        f = bar

        #print('globals', inspect.stack()[0][0].f_globals)
        #print('locals', inspect.stack()[0][0].f_locals)

        assert(inspect.stack()[0][0].f_globals == globals())
        assert(inspect.stack()[0][0].f_locals == locals())

        in_a_haystack = lambda: value == needle and key != 'needle'

        for needle in (a, foo, bar, d, f, ):
            print("needle => '%r'" % (needle, ))
            print([key for key, value in locals().iteritems() if in_a_haystack()])
            print([key for key, value in globals().iteritems() if in_a_haystack()])


    foo()

    通常,当您想要这样做时,您创建一个类来保存所有这些函数,并用一些清晰的前缀cmd_或类似的名称命名它们。然后从命令中获取字符串,并尝试从带有cmd_前缀的类中获取该属性。现在您只需要向类中添加一个新的函数/方法,它对调用方是可用的。您可以使用文档字符串自动创建帮助文本。

    如其他答案中所述,您可以使用globals()和模块中的常规函数执行相同的方法,以更接近于您所要求的内容。

    像这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class Tasks:
        def cmd_doit(self):
            # do it here

    func_name = parse_commandline()
    try:
        func = getattr('cmd_' + func_name, Tasks())
    except AttributeError:
        # bad command: exit or whatever
    func()

    变量名可以在globals()和locals()dicts中找到。但他们不会给你你想要的东西。"bla"将包含我的_列表中每个项目的值,而不是变量。