Convert Variable Name to String?

Viewed 148243

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'
22 Answers

Totally possible with the python-varname package (python3):

from varname import nameof

s = 'Hey!'

print (nameof(s))

Output:

s

Install:

pip3 install varname

Or get the package here:

https://github.com/pwwang/python-varname

By using the the unpacking operator:

>>> def tostr(**kwargs):
    return kwargs

>>> var = {}
>>> something_else = 3
>>> tostr(var = var,something_else=something_else)
{'var' = {},'something_else'=3}

I don't know it's right or not, but it worked for me

def varname(variable):
    for name in list(globals().keys()):
        expression = f'id({name})'
        if id(variable) == eval(expression):
            return name

it is possible to a limited extent. the answer is similar to the solution by @tamtam . The given example assumes the following assumptions -

  • You are searching for a variable by its value
  • The variable has a distinct value
  • The value is in the global namespace

Example:

testVar         = "unique value"
varNameAsString = [k for k,v in globals().items() if v == "unique value"]
#
# the variable "varNameAsString" will contain all the variable name that matches
# the value "unique value"
# for this example, it will be a list of a single entry "testVar"
#
print(varNameAsString)

Output : ['testVar']

You can extend this example for any other variable/data type

I'd like to point out a use case for this that is not an anti-pattern, and there is no better way to do it.

This seems to be a missing feature in python.

There are a number of functions, like patch.object, that take the name of a method or property to be patched or accessed.

Consider this:

patch.object(obj, "method_name", new_reg)

This can potentially start "false succeeding" when you change the name of a method. IE: you can ship a bug, you thought you were testing.... simply because of a bad method name refactor.

Now consider: varname. This could be an efficient, built-in function. But for now it can work by iterating an object or the caller's frame:

Now your call can be:

patch.member(obj, obj.method_name, new_reg)

And the patch function can call:

varname(var, obj=obj)

This would: assert that the var is bound to the obj and return the name of the member. Or if the obj is not specified, use the callers stack frame to derive it, etc.

Could be made an efficient built in at some point, but here's a definition that works. I deliberately didn't support builtins, easy to add tho:

Feel free to stick this in a package called varname.py, and use it in your patch.object calls:

patch.object(obj, varname(obj, obj.method_name), new_reg)

Note: this was written for python 3.

import inspect

def _varname_dict(var, dct):
    key_name = None
    for key, val in dct.items():
        if val is var:
            if key_name is not None:
                raise NotImplementedError("Duplicate names not supported %s, %s" % (key_name, key))
            key_name = key
    return key_name

def _varname_obj(var, obj):
    key_name = None
    for key in dir(obj):
        val = getattr(obj, key)
        equal = val is var
        if equal:
            if key_name is not None:
                raise NotImplementedError("Duplicate names not supported %s, %s" % (key_name, key))
            key_name = key
    return key_name

def varname(var, obj=None):
    if obj is None:
        if hasattr(var, "__self__"):
            return var.__name__
        caller_frame = inspect.currentframe().f_back
        try:
            ret = _varname_dict(var, caller_frame.f_locals)
        except NameError:
            ret = _varname_dict(var, caller_frame.f_globals)
    else:
        ret = _varname_obj(var, obj)
    if ret is None:
        raise NameError("Name not found. (Note: builtins not supported)")
    return ret

It's not very Pythonesque but I was curious and found this solution. You need to duplicate the globals dictionary since its size will change as soon as you define a new variable.

def var_to_name(var):
    # noinspection PyTypeChecker
    dict_vars = dict(globals().items())

    var_string = None

    for name in dict_vars.keys():
        if dict_vars[name] is var:
            var_string = name
            break

    return var_string


if __name__ == "__main__":
    test = 3
    print(f"test = {test}")
    print(f"variable name: {var_to_name(test)}")

which returns:

test = 3
variable name: test

To get the variable name of var as a string:

var = 1000
var_name = [k for k,v in locals().items() if v == var][0] 
print(var_name) # ---> outputs 'var'

Thanks @restrepo, this was exactly what I needed to create a standard save_df_to_file() function. For this, I made some small changes to your tostr() function. Hope this will help someone else:

def variabletostr(**df):
        variablename = list(df.keys())[0]
        return variablename
    
    variabletostr(df=0)

The original question is pretty old, but I found an almost solution with Python 3. (I say almost because I think you can get close to a solution but I do not believe there is a solution concrete enough to satisfy the exact request).

First, you might want to consider the following:

  • objects are a core concept in Python, and they may be assigned a variable, but the variable itself is a bound name (think pointer or reference) not the object itself
  • var is just a variable name bound to an object and that object could have more than one reference (in your example it does not seem to)
  • in this case, var appears to be in the global namespace so you can use the global builtin conveniently named global
  • different name references to the same object will all share the same id which can be checked by running the id builtin id like so: id(var)

This function grabs the global variables and filters out the ones matching the content of your variable.

def get_bound_names(target_variable):
    '''Returns a list of bound object names.'''
    return [k for k, v in globals().items() if v is target_variable]

The real challenge here is that you are not guaranteed to get back the variable name by itself. It will be a list, but that list will contain the variable name you are looking for. If your target variable (bound to an object) is really the only bound name, you could access it this way:

bound_names = get_variable_names(target_variable)
var_string = bound_names[0]

This module works for converting variables names to a string: https://pypi.org/project/varname/

Use it like this:

from varname import nameof

variable=0

name=nameof(variable)

print(name)

//output: variable

Install it by:

pip install varname
Related