【问题标题】:function to print name of passed variable打印传递变量名称的函数
【发布时间】:2015-07-11 14:44:39
【问题描述】:

我最初的目标是创建一个函数来打印给定对象的类型和内存地址。为了尽可能通用,我还希望包含变量名,如下所示:

>>> a=10
>>> print type_addr(a)
a: int, 0x13b8080

为此,我需要知道传递给此函数的变量的名称。这个page 建议以下代码(它有点修改版本,但想法保持不变,迭代locals().iteritems()。我知道它不安全,它在给定链接上提到了几个陷阱,但我的计划是改进它) :

#!/usr/bin/python

a = b = c = 10
b = 11

for k, v in list(locals().iteritems()):
  if v is b:
  # if id(v) == id(b):
    print "k: %s" % k
    print "v: %s" % v
    print "a: %s" % hex(id(k))

以上代码的输出为:

k: b
v: 11
a: 0x7fece0f305f8

我的下一个目标是制作子程序,它会给我想要的结果,所以我尝试将它包装到子程序中:

#!/usr/bin/python

a = b = c = 10
b = 11

def addr_type(obj):
  for k, v in list(locals().iteritems()):
    # if id(v) == id(a):
    if v is obj:
      print "k: %s" % k
      print "v: %s" % v
      print "a: %s" % hex(id(k))

for k, v in list(locals().iteritems()):
  if v is b:
  # if id(v) == id(b):
    print "k: %s" % k
    print "v: %s" % v
    print "a: %s" % hex(id(k))

print "#################"

addr_type(b)

上面代码的输出是:

k: b
v: 11
a: 0x7fc9253715f8
#################
k: obj
v: 11
a: 0x7fc9253198a0

如您所见,变量的名称和地址都不相同。然后我开始深入挖掘并尝试以下操作:

#!/usr/bin/python
a = b = c = 10
b = 11

for k, v in list(locals().iteritems()):
   print "k: %s" % k
   print "v: %s" % v
   print "a: %s" % hex(id(k))
   print "##############"


if a is b:
    print "a and b is the same objects"
else:
    print "a and b is NOT the same objects"


if a is c:
    print "a and c is the same objects"
else:
    print "a and c is NOT the same objects"


if b is c:
    print "b and c is the same objects"
else:
    print "b and c is NOT the same objects"

返回:

k: a
v: 10
a: 0x7ff07d54b5d0
##############
k: c
v: 10
a: 0x7ff07d54bbe8
##############
k: b
v: 11
a: 0x7ff07d54b5f8
##############
<Some loaded modules here but nothing interesting>
##############
a and b is NOT the same objects
a and c is the same objects
b and c is NOT the same objects

问题:

  1. 如何重写工作代码并制作将打印传递变量名称的函数?
  2. 为什么相同的对象有不同的地址?

【问题讨论】:

    标签: python


    【解决方案1】:

    TL; DR: 由于名称绑定在 Python 中的工作方式,您通常无法获取传递给函数的变量的名称。一些 Python 对象确实具有 name 属性,但这与您在赋值语句中绑定到对象的名称不同。

    当您打印ids 时,您的代码给了您误导性信息。您实际上不是在打印对象的ids,而是在打印locals()dict 中名称字符串的ids。

    此外,当您在函数中获得locals() 时,它会向您显示该函数的本地内容。因此,要获取有关函数之外的本地对象的信息,您需要让函数访问您感兴趣的实际 locals() dict。

    下面是您的代码的修改版本,它修复了id 的内容,并将locals() 数据的副本传递给addr_type 函数。

    请注意,当您调用 ac 的函数时,它会打印两个名称的信息。

    我还添加了几行代码,说明如何使用函数的__name__ 属性打印函数的名称。请注意,当我们打印new_name.__name__ 时,它只会打印addr_type,因为这是函数的__name__ 属性的值,所以我们还将它绑定到new_name 的事实是无关紧要的。

    #!/usr/bin/env python
    
    def addr_type(context, obj):
        for k, v in context:
            # if id(v) == id(a):
            if v is obj:
                print "k: %s" % k
                print "v: %s" % v
                print "a: %s" % hex(id(v))
                print
        print 15 * "-"
    
    
    a = b = c = 10
    b = 11
    
    for k, v in list(locals().iteritems()):
        if k.startswith('__'):
            continue
        print "k: %s" % k
        print "v: %s" % v
        print "a: %s" % hex(id(v))
        print "##############"
    
    print
    
    if a is b:
        print "a and b is the same objects"
    else:
        print "a and b is NOT the same objects"
    
    
    if a is c:
        print "a and c is the same objects"
    else:
        print "a and c is NOT the same objects"
    
    
    if b is c:
        print "b and c is the same objects"
    else:
        print "b and c is NOT the same objects"
    
    print
    
    context = list(locals().iteritems())
    addr_type(context, a)
    addr_type(context, b)
    addr_type(context, c)
    
    new_name = addr_type
    print 'Function names =', addr_type.__name__, new_name.__name__
    

    输出

    k: a
    v: 10
    a: 0x8dafd24
    ##############
    k: c
    v: 10
    a: 0x8dafd24
    ##############
    k: b
    v: 11
    a: 0x8dafd18
    ##############
    k: addr_type
    v: <function addr_type at 0xb748e17c>
    a: 0xb748e17cL
    ##############
    
    a and b is NOT the same objects
    a and c is the same objects
    b and c is NOT the same objects
    
    k: a
    v: 10
    a: 0x8dafd24
    
    k: c
    v: 10
    a: 0x8dafd24
    
    ---------------
    k: b
    v: 11
    a: 0x8dafd18
    
    ---------------
    k: a
    v: 10
    a: 0x8dafd24
    
    k: c
    v: 10
    a: 0x8dafd24
    
    ---------------
    Function names = addr_type addr_type
    

    您可能会发现学习由 Stack Overflow 资深人士 Ned Batchelder 撰写的优秀文章 Facts and myths about Python names and values 会有所帮助。


    更新

    这是一个简单的函数show,它会打印您传递给它的名称,以及您也传递给它的字典中该名称的值。通过将locals() 字典传递给它,它可以在本地范围内按名称查找对象。在函数内部,这是函数的局部变量。在函数之外,这就是所有的全局变量。

    def show(*args, names):
        for s in args:
            print(s, names.get(s, 'Not found!'))
    
    def test(t):
        a = 1
        b = 2
        c = 3
        show('a', 'b', 'c', 't', names=locals())
    
    test(42)
    

    输出

    a 1
    b 2
    c 3
    t 42
    

    【讨论】:

      【解决方案2】:

      我从问题的第二部分开始。这里的问题是,您正在打印hex(id(k))。你应该打印hex(id(v))

      locals()中搜索名称不是一个好主意

      更新并返回一个表示当前本地符号表的字典。

      如果你在函数内部调用locals(),你只会得到在这个范围内定义的变量。这就是它打印的原因

      k: obj
      v: 11
      a: 0x7fc9253198a0
      

      您必须将 locals() 字典传递给 type_addr() 才能获得有关变量来源范围的一些信息。

      a = 7
      type_addr(a, locals())
      

      另一方面,如果您有多个具有相同值的变量,您很可能会得到错误的名称(如果您不打印所有名称)。

      所以关注The Zen of Python

      显式优于隐式。

      我建议将名称传递给显式打印。

      def type_addr(obj, msg):
          print '%s: %s %s' % (msg, type(obj).__name__, hex(id(obj)))
      
      a = 11
      type_addr(a, 'a')
      

      【讨论】:

      • 谢谢。我对此投了赞成票,但将@PM 2Ring 帖子标记为答案,因为它提供了更多详细信息。再次感谢您。
      猜你喜欢
      • 2012-04-13
      • 1970-01-01
      • 2013-06-04
      • 2017-07-21
      • 2014-08-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-13
      • 1970-01-01
      相关资源
      最近更新 更多