How to Get Value When a Variable Name Is Passed as a String

Getting the name of a variable as a string

TL;DR

Use the Wrapper helper from python-varname:

from varname.helpers import Wrapper

foo = Wrapper(dict())

# foo.name == 'foo'
# foo.value == {}
foo.value['bar'] = 2

For list comprehension part, you can do:

n_jobs = Wrapper(<original_value>) 
users = Wrapper(<original_value>)
queues = Wrapper(<original_value>)
priorities = Wrapper(<original_value>)

list_of_dicts = [n_jobs, users, queues, priorities]
columns = [d.name for d in list_of_dicts]
# ['n_jobs', 'users', 'queues', 'priorities']
# REMEMBER that you have to access the <original_value> by d.value

I am the author of the python-varname package. Please let me know if you have any questions or you can submit issues on Github.

The long answer

Is it even possible?

Yes and No.

We are retrieving the variable names at runtime, so we need a function to be called to enable us to access the previous frames to retrieve the variable names. That's why we need a Wrapper there. In that function, at runtime, we are parsing the source code/AST nodes in the previous frames to get the exact variable name.

However, the source code/AST nodes in the previous frames are not always available, or they could be modified by other environments (e.g: pytest's assert statement). One simple example is that the codes run via exec(). Even though we are still able to retrieve some information from the bytecode, it needs too much effort and it is also error-prone.

How to do it?

First of all, we need to identify which frame the variable is given. It's not always simply the direct previous frame. For example, we may have another wrapper for the function:

from varname import varname

def func():
return varname()

def wrapped():
return func()

x = wrapped()

In the above example, we have to skip the frame inside wrapped to get to the right frame x = wrapped() so that we are able to locate x. The arguments frame and ignore of varname allow us to skip some of these intermediate frames. See more details in the README file and the API docs of the package.

Then we need to parse the AST node to locate where the variable is assigned value (function call) to. It's not always just a simple assignment. Sometimes there could be complex AST nodes, for example, x = [wrapped()]. We need to identify the correct assignment by traversing the AST tree.

How reliable is it?

Once we identify the assignment node, it is reliable.

varname is all depending on executing package to look for the node. The node executing detects is ensured to be the correct one (see also this).

It partially works with environments where other AST magics apply, including pytest, ipython, macropy, birdseye, reticulate with R, etc. Neither executing nor varname is 100% working with those environments.

Do we need a package to do it?

Well, yes and no, again.

If your scenario is simple, the code provided by @juan Isaza or @scohe001 probably is enough for you to work with the case where a variable is defined at the direct previous frame and the AST node is a simple assignment. You just need to go one frame back and retrieve the information there.

However, if the scenario becomes complicated, or we need to adopt different application scenarios, you probably need a package like python-varname, to handle them. These scenarios may include to:

  1. present more friendly messages when the source code is not available or AST nodes are not accessible
  2. skip intermediate frames (allows the function to be wrapped or called in other intermediate frames)
  3. automatically ignores calls from built-in functions or libraries. For example: x = str(func())
  4. retrieve multiple variable names on the left-hand side of the assignment
  5. etc.

How about the f-string?

Like the answer provided by @Aivar Paalberg. It's definitely fast and reliable. However, it's not at runtime, meaning that you have to know it's foo before you print the name out. But with varname, you don't have to know that variable is coming:

from varname import varname

def func():
return varname()

# In external uses
x = func() # 'x'
y = func() # 'y'

Finally

python-varname is not only able to detect the variable name from an assignment, but also:

  • Retrieve variable names directly, using nameof
  • Detect next immediate attribute name, using will
  • Fetch argument names/sources passed to a function using argname

Read more from its documentation.

However, the final word I want to say is that, try to avoid using it whenever you can.

Because you can't make sure that the client code will run in an environment where the source node is available or AST node is accessible. And of course, it costs resources to parse the source code, identify the environment, retrieve the AST nodes and evaluate them when needed.

How to get the value of a variable given its name in a string?

If it's a global variable, then you can do:

>>> a = 5
>>> globals()['a']
5

A note about the various "eval" solutions: you should be careful with eval, especially if the string you're evaluating comes from a potentially untrusted source -- otherwise, you might end up deleting the entire contents of your disk or something like that if you're given a malicious string.

(If it's not global, then you'll need access to whatever namespace it's defined in. If you don't have that, there's no way you'll be able to access it.)

Variable name as a string in Javascript

Typically, you would use a hash table for a situation where you want to map a name to some value, and be able to retrieve both.

var obj = { myFirstName: 'John' };

obj.foo = 'Another name';

for(key in obj)

console.log(key + ': ' + obj[key]);

Calling a variable by passing variable name as a string

I was able to resolve the problem by using ${$arg} notation as described by @waterloomatt above.

How to get a variable value if variable name is stored as string?

You can use ${!a}:

var1="this is the real value"
a="var1"
echo "${!a}" # outputs 'this is the real value'

This is an example of indirect parameter expansion:

The basic form of parameter expansion is ${parameter}. The value of
parameter is substituted.

If the first character of parameter is an exclamation point (!), it
introduces a level of variable indirection. Bash uses the value of the
variable formed from the rest of parameter as the name of the
variable; this variable is then expanded and that value is used in the
rest of the substitution, rather than the value of parameter itself.

Convert Variable Name to String?

TL;DR: Not possible. See 'conclusion' at the end.


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 easily lead to unsecured code. Care must be taken to not feed the eval() function with just about anything, especially unfiltered external-input data.

get variable name into string in javascript

Use the code below.

const abc = '123';

const def = '456';

const varToString = varObj => Object.keys(varObj)[0]

alert ("value of " +varToString({def}) +" is "+ def +" and value of " +varToString({abc})+" is "+abc );

Passing string for variable name Python

As others have suggested, you will likely want an answer that uses a dictionary to lookup numbers given letters.

##----------------------
## hardcode your input() for testing
##----------------------
#input_numbers = input()
#input_letters = input()
input_numbers = "5 20 16"
input_letters = "CAB"

input_numbers_list = input_numbers.split(" ")
input_letters_list = list(input_letters) # not technically needed
##----------------------

##----------------------
## A dictionary comprehension
# used to construct a lookup of character to number
##----------------------
lookup = {
letter: number
for letter, number
in zip(
sorted(input_letters_list),
sorted(input_numbers_list, key=int)
)
}
##----------------------

##----------------------
## use our original letter order and the lookup to produce numbers
##----------------------
result = " ".join(lookup[a] for a in input_letters_list)
##----------------------

print(result)

This will give you your requested output of:

20 5 16

There is a lot going on with the construction of the dictionary lookup, so let's unpack it a bit.

First of, it is based on calling zip(). This function takes two "lists" and pairs their elements up creating a new "list". I use "list" in quotes as it is more like iterables and generators. Anyways. let's take a closer look at:

list(zip(["a","b","c"], ["x","y","z"]))

this is going to give us:

[
('a', 'x'),
('b', 'y'),
('c', 'z')
]

So this is how we are going to pairwise combine our numbers and letters together.

But before we do that, it is important to make sure that we are going to pair up the "largest" letters with the "largest" numbers. To ensure that we will get a sorted version of our two lists:

list(
zip(
sorted(input_letters_list), #ordered by alphabet
sorted(input_numbers_list, key=int) #ordered numerically
)
)

gives us:

[
('A', '5'),
('B', '16'),
('C', '20')
]

Now we can feed that into our dictionary comprehension (https://docs.python.org/3/tutorial/datastructures.html).

This will construct a dictionary with keys of our letters in the above zip() and values of our numbers.

lookup = {
letter: number
for letter, number
in zip(
sorted(input_letters_list),
sorted(input_numbers_list, key=int)
)
}
print(lookup)

Will give us our lookup dictionary:

{
'A': '5',
'B': '16',
'C': '20'
}

Note that our zip() technically gives us back a list of tuples and we could also use dict() to cast them to our lookup.

lookup = dict(zip(
sorted(input_letters_list),
sorted(input_numbers_list, key=int)
))
print(lookup)

also gives us:

{
'A': '5',
'B': '16',
'C': '20'
}

But I'm not convinced that clarifies what is going on or not. It is the same result though so if you feel that is clearer go for it.

Now all we need to do is go back to our original input and take the letters one by one and feed them into our lookup to get back numbers.

Hope that helps.



Related Topics



Leave a reply



Submit