Function for printing the name of the passed variable
My original goal was to create a function that will print the type and memory address of a given object. To be as versatile as possible, I would like to include the variable name as well, something like this:
>>> a=10
>>> print type_addr(a)
a: int, 0x13b8080
To do this, I need to know the name of the variable passed to this function. This page offers the following code (this is a slightly modified version, but the idea remains the same, iterated over locals().iteritems()
. I know it is unsafe and it has a few pitfalls mentioned in this link, but my plans were to improve it):
#!/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))
Output of the code above:
k: b
v: 11
a: 0x7fece0f305f8
My next goal was to make a subroutine that gives me the desired result, so I tried to include it in the subroutine:
#!/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)
Output code above:
k: b
v: 11
a: 0x7fc9253715f8
#################
k: obj
v: 11
a: 0x7fc9253198a0
As you can see, neither the variable name nor the address match. Then I started digging deeper and tried the following:
#!/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"
What came back:
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
Questions:
- How do I rewrite the working code and make a function that will print the name of the passed variable?
- Why do the same objects have different addresses?
source to share
TL; DR: Because of the way name binding works in Python, you usually cannot get the name of the variable you are passing to the function. Some Python objects have a name attribute, but this is not the same as the name that you associate with the object in an assignment statement.
You enter a misleading code when printing id
s. You are not actually printing id
objects, you are printing id
name strings in locals()
dict
.
Also, when you get locals()
inside a function, it shows you stuff that is local to the function. Therefore, in order to get information about local objects outside of a function, you need to give the function access to the actual locals()
dict you are interested in.
Below is a modified version of your code that fixes stuff id
and also passes a copy of the data locals()
to the function addr_type
.
Note that when you call a function for a
or c
, it prints information for both names.
I've also added a few lines that show how you can print the name of a function using its attribute __name__
. Note that when we print new_name.__name__
, it just prints addr_type
, since this is the value of the attribute __name__
, the fact that we also bound it with new_name
doesn't matter.
#!/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__
Output
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
You might find it helpful to check out the excellent article Facts and Myths about Python Names and Values written by Stack Overflow veteran Ned Batchelder.
Update
Here's a simple function show
that prints the names you pass along with the value of that name in the dictionary you pass as well. By passing this dictionary locals()
, it allows you to find objects by name in the local scope. Inside the function is a function of local variables. Outside of a function, that's all global variables.
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)
Output
a 1
b 2
c 3
t 42
source to share
I start with the second part of the question. The problem here is what you are typing hex(id(k))
. You should type hex(id(v))
instead.
It is not recommended to look for a name in locals()
Update and return a dictionary representing the current local symbol table.
If you call locals()
inside a function, you will only get the variables defined in that scope. This is why it prints
k: obj
v: 11
a: 0x7fc9253198a0
You will need to pass the dictionary locals()
to type_addr()
in order to get some information about the scope the variable came from.
a = 7 type_addr(a, locals())
On the other hand, you are more likely to get the wrong names (unless you print them all) if you have more than one variable with the same value.
So, following Zen of Python
Explicit is better than implicit.
I would suggest passing the name to print explicitly.
def type_addr(obj, msg):
print '%s: %s %s' % (msg, type(obj).__name__, hex(id(obj)))
a = 11
type_addr(a, 'a')
source to share