views:

900

answers:

6

I would like to convert a python variable name into the string equivalent as shown. Any ideas how?

var = {}
print ???  # Would like to see 'var'
something_else = 3
print ???  # Would print 'something_else'
A: 
print "var"
print "something_else"

Or did you mean something_else?

Jonathan Feinberg
+5  A: 

You somehow have to refer to the variable you want to print the name of. So it would look like:

print varname(something_else)

There is no such function, but if there were it would be kind of pointless. You have to type out something_else, so you can as well just type quotes to the left and right of it to print the name as a string:

print "something_else"
sth
+5  A: 

This is not possible.

In Python, there really isn't any such thing as a "variable". What Python really has are "names" which can have objects bound to them. It makes no difference to the object what names, if any, it might be bound to. It might be bound to dozens of different names, or none.

Consider this example:

foo = 1
bar = 1
baz = 1

Now, suppose you have the integer object with value 1, and you want to work backwards and find its name. What would you print? Three different names have that object bound to them, and all are equally valid.

In Python, a name is a way to access an object, so there is no way to work with names directly. There might be some clever way to hack the Python bytecodes or something to get the value of the name, but that is at best a parlor trick.

If you know you want print foo to print "foo", you might as well just execute print "foo" in the first place.

EDIT: I have changed the wording slightly to make this more clear. Also, here is an even better example:

foo = 1
bar = foo
baz = foo

In practice, Python reuses the same object for integers with common values like 0 or 1, so the first example should bind the same object to all three names. But this example is crystal clear: the same object is bound to foo, bar, and baz.

steveha
objects don't have names, names have objects
gnibbler
There is a way to work with these names, the globals() dict, but it's not clear how to use that to do what the OP asked.
Todd
of course, *it is possible*. and quite trivial at that (it solves the OP's problem, anyway). it's just something no one should want to do.
SilentGhost
+2  A: 

Technically the information is available to you, but as others have asked, how would you make use of it in a sensible way?

>>> x = 52
>>> globals()
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', 
'x': 52, '__doc__': None, '__package__': None}

This shows that the variable name is present as a string in the globals() dictionary.

>>> globals().keys()[2]
'x'

In this case it happens to be the third key, but there's no reliable way to know where a given variable name will end up

>>> for k in globals().keys():
...   if not k.startswith("_"):
...     print k
...
x
>>>

You could filter out system variables like this, but you're still going to get all of your own items. Just running that code above created another variable "k" that changed the position of "x" in the dict.

But maybe this is a useful start for you. If you tell us what you want this capability for, more helpful information could possibly be given.

Todd
If I'm willing to jump through a million hoops I can write some code to actually modify a variable if given its name as a string, but this is an awful idea and is wildly unsafe and no one should ever do it.
Chris Lutz
Chris, your comment doesn't seem to relate to my answer or the original question. Who are you scolding and why? You don't know if he's writing production code or just experimenting with language features for his own education.
Todd
+2  A: 

What are you trying to achieve? There is absolutely no reason to ever do what you describe, and there is likely a much better solution to the problem you're trying to solve..

The most obvious alternative to what you request is a dictionary. For example:

>>> my_data = {'var': 'something'}
>>> my_data['something_else'] = 'something'
>>> print my_data.keys()
['var', 'something_else']
>>> print my_data['var']
something

Mostly as a.. challenge, I implemented your desired output. Do not use this code, please!

#!/usr/bin/env python2.6
class NewLocals:
    """Please don't ever use this code.."""
    def __init__(self, initial_locals):
        self.prev_locals = list(initial_locals.keys())

    def show_new(self, new_locals):
        output = ", ".join(list(set(new_locals) - set(self.prev_locals)))
        self.prev_locals = list(new_locals.keys())
        return output
# Set up
eww = None
eww = NewLocals(locals())

# "Working" requested code

var = {}

print eww.show_new(locals())  # Outputs: var

something_else = 3
print eww.show_new(locals()) # Outputs: something_else

# Further testing

another_variable = 4
and_a_final_one = 5

print eww.show_new(locals()) # Outputs: another_variable, and_a_final_one
dbr
This is pretty cool, but quite as automatic as I would like. Thanks for the tip =]
Brandon Pelfrey
+1  A: 

There is an usage scenario where you might need this. I'm not implying there are not better ways or achieving the same functionality.

This would be useful in order to 'dump' an arbitrary list of dictionaries in case of error, in debug modes and other similar situations.

What would be needed, is the reverse of the eval() function:

get_indentifier_name_missing_function()

which would take an identifier name ('variable','dictionary',etc) as an argument, and return a string containing the identifier’s name.


Consider the following current state of affairs:

random_function(argument_data)

If one is passing an identifier name ('function','variable','dictionary',etc) argument_data to a random_function() (another identifier name), one actually passes an identifier (e.g.: <argument_data object at 0xb1ce10>) to another identifier (e.g.: <function random_function at 0xafff78>):

<function random_function at 0xafff78>(<argument_data object at 0xb1ce10>)

From my understanding, only the memory address is passed to the function:

<function at 0xafff78>(<object at 0xb1ce10>)

Therefore, one would need to pass a string as an argument to random_function() in order for that function to have the argument's identifier name:

random_function('argument_data')

Inside the random_function()

def random_function(first_argument):

, one would use the already supplied string 'argument_data' to:

  1. serve as an 'identifier name' (to display, log, string split/concat, whatever)
  2. feed the eval() function in order to get a reference to the actual identifier, and therefore, a reference to the real data:

    print("Currently working on", first_argument)
    some_internal_var = eval(first_argument)
    print("here comes the data: " + str(some_internal_var))
    

Unfortunately, this doesn't work in all cases. It only works if the random_function() can resolve the 'argument_data' string to an actual identifier. I.e. If argument_data identifier name is available in the random_function()'s namespace.

This isn't always the case:

# main1.py
import some_module1

argument_data = 'my data'

some_module1.random_function('argument_data')


# some_module1.py
def random_function(first_argument):
    print("Currently working on", first_argument)
    some_internal_var = eval(first_argument)
    print("here comes the data: " + str(some_internal_var))
######

Expected results would be:

Currently working on: argument_data
here comes the data: my data

Because argument_data identifier name is not available in the random_function()'s namespace, this would yield instead:

Currently working on argument_data
Traceback (most recent call last):
  File "~/main1.py", line 6, in <module>
    some_module1.random_function('argument_data')
  File "~/some_module1.py", line 4, in random_function
    some_internal_var = eval(first_argument)
  File "<string>", line 1, in <module>
NameError: name 'argument_data' is not defined

Now, consider the hypotetical usage of a get_indentifier_name_missing_function() which would behave as described above.

Here's a dummy Python 3.0 code: .

# main2.py
import some_module2
some_dictionary_1       = { 'definition_1':'text_1',
                            'definition_2':'text_2',
                            'etc':'etc.' }
some_other_dictionary_2 = { 'key_3':'value_3',
                            'key_4':'value_4', 
                            'etc':'etc.' }
#
# more such stuff
#
some_other_dictionary_n = { 'random_n':'random_n',
                            'etc':'etc.' }

for each_one_of_my_dictionaries in ( some_dictionary_1,
                                     some_other_dictionary_2,
                                     ...,
                                     some_other_dictionary_n ):
    some_module2.some_function(each_one_of_my_dictionaries)


# some_module2.py
def some_function(a_dictionary_object):
    for _key, _value in a_dictionary_object.items():
        print( get_indentifier_name_missing_function(a_dictionary_object)    +
               "    " +
               str(_key) +
               "  =  " +
               str(_value) )
######

Expected results would be:

some_dictionary_1    definition_1  =  text_1
some_dictionary_1    definition_2  =  text_2
some_dictionary_1    etc  =  etc.
some_other_dictionary_2    key_3  =  value_3
some_other_dictionary_2    key_4  =  value_4
some_other_dictionary_2    etc  =  etc.
......
......
......
some_other_dictionary_n    random_n  =  random_n
some_other_dictionary_n    etc  =  etc.

Unfortunately, get_indentifier_name_missing_function() would not see the 'original' identifier names (some_dictionary_,some_other_dictionary_2,some_other_dictionary_n). It would only see the a_dictionary_object identifier name.

Therefore the real result would rather be:

a_dictionary_object    definition_1  =  text_1
a_dictionary_object    definition_2  =  text_2
a_dictionary_object    etc  =  etc.
a_dictionary_object    key_3  =  value_3
a_dictionary_object    key_4  =  value_4
a_dictionary_object    etc  =  etc.
......
......
......
a_dictionary_object    random_n  =  random_n
a_dictionary_object    etc  =  etc.

So, the reverse of the eval() function won't be that useful in this case.


Currently, one would need to do this:

# main2.py same as above, except:

    for each_one_of_my_dictionaries_names in ( 'some_dictionary_1',
                                               'some_other_dictionary_2',
                                               '...',
                                               'some_other_dictionary_n' ):
        some_module2.some_function( { each_one_of_my_dictionaries_names :
                                     eval(each_one_of_my_dictionaries_names) } )


    # some_module2.py
    def some_function(a_dictionary_name_object_container):
        for _dictionary_name, _dictionary_object in a_dictionary_name_object_container.items():
            for _key, _value in _dictionary_object.items():
                print( str(_dictionary_name) +
                       "    " +
                       str(_key) +
                       "  =  " +
                       str(_value) )
    ######

In conclusion:

  • Python passes only memory addresses as arguments to functions.
  • Strings representing the name of an identifier, can only be referenced back to the actual identifier by the eval() function if the name identifier is available in the current namespace.
  • A hypothetical reverse of the eval() function, would not be useful in cases where the identifier name is not 'seen' directly by the calling code. E.g. inside any called function.
  • Currently one needs to pass to a function:
    1. the string representing the identifier name
    2. the actual identifier (memory address)

This can be achieved by passing both the 'string' and eval('string') to the called function at the same time. I think this is the most 'general' way of solving this egg-chicken problem across arbitrary functions, modules, namespaces, without using corner-case solutions. The only downside is the use of the eval() function which may easly lead to unsecured code. Care must be taken to not feed the eval() fuction with just about anything, especially unfiltered external-input data.

Petru Zaharia