return variable name from outside of function, as string inside python function

3.6k views Asked by At

So I have created a function that applies an action (in this case point wise multiplication of an array with a sinusoid, but that does not matter for my question) to an array.

Now I have created another function with which I want to create a string of python code to apply the first function multiple times later-on. The input of the second function can be either a string or an array, so that I can use the second function on its own output as well, if need be. My method of getting the variable name in a string works outside of the function.

Input :

var = np.array([[1,3],[2,4]]) # or sometimes var = 'a string'

if type(var)==str:
    var_name = var
else:
    var_name = [ k for k,v in locals().items() if v is var][0]

var_name

Output :

'var'

So here var is the variable (either array or string) supplied to the function, in this case an array. The if statement nicely returns me its name.

However when I use this inside my function, no matter what input I give it, it actually seems to look for var in locals(). Somehow it does not take var from the function input.

Definition :

def functionTWO(var, listoflistsofargs=None):
    if type(var)==str:
        var_name = var
    else:
        var_name = [ k for k,v in locals().items() if v is var][0]
    if listoflistsofargs==None:
        return var_name
    command = []
    for i in range(len(listoflistsofargs)):
        if i==0:
            command.append('functionONE(')
            command.append(var_name)
            command.append(',%.17f, %.17f)' % tuple(listoflistsofargs[i]))
        else:
            command.insert(0,'functionONE(')
            command.append(',%.17f, %.17f)' % tuple(listoflistsofargs[i]))
    ''.join(command)
    command[0] = var_name + ' + ' + command[0]
    return ''.join(command)

Input :

somearray = np.array([[1,2,3],[1,2,3],[1,2,3]])
args = [[1,3],[6,5]]
command = functionTWO(somearray, args)
command

Output :

NameError: name 'var' is not defined

Wanted output :

'functionONE(functionONE(somearray, 1, 3), 6, 5)'

Why is listoflistsofargs taken from the function input and var not? I specify var in the listcomprehension in the definition of functionTWO. Normally when I use list comprehensions with function inputs it works fine. Does anybody know why this isnt the case here? Thank you in advance!

EDIT : So I guess the answer is dont. The implementation of classes by Marcin looks much cleaner and about the same order of amount of code. Too bad I couldnt get this to work inside a function. For other donts (actually other ideas) about using variable names as strings there is this question, where I got the above list comprehension for variable names.

3

There are 3 answers

4
Marcin On BEST ANSWER

You cannot pass a variable as a string*, and you should not do so.

If you want to pass a value between functions, the normal way is to pass it in as a parameter, and out as a return value.

If that is inconvenient, the usual solution is an object: define a class which carries both the shared variable, and methods which act on the variable.

If you need to create command objects, it is much better to do so in a structured way. For example, if you want to pass a function, and parameters, you can literally just pass the function object and the parameters in a tuple:

def foo():
    return (functionONE,somearray,1,3)

command = foo()
command[0](*command[1:])

If you want to embed such commands within commands, you'll likely want to wrap that up with a class, so you can recursively evaluate the parameters. In fact, here's a little evaluator:

def evaluator(object):
    def __init__(self,func=None,params=None):
        self.func = func
        self.params = params

    def eval(self,alternativeparams=None):
        if alternativeparams is not None:
           params = alternativeparams
        else:
           params = self.params

        if params is not None:
           evaluatedparams = (item() if callable(item) else item for item in params)
        else: evaluatedparams = None

        if func is not None:
           return self.func(*(evaluatedparams or ()))
        else: return evaluatedparams
    def __call__(self, *params):
        return self.eval(params if params else None)

Although there are hacks by which you can pass references to local variables out of a function, these are not a great idea, because you end up creating your own half-baked object system which is more difficult to understand.

* This is because a variable has a name (which is a string) and a context, which maps names to strings. So, you need, at least to pass a tuple to truly pass a variable.

0
cacti5 On

presenter's answer works in python 3

def get_variable_name(*variable):
    return [ k for k,v in globals().items() if v is variable[0]][0]

example:

>> test = 'sample string'
>> get_variable_name(test)
'test'
>>

The only issue is it is a little messy.

0
presenter On

The reason you get the error NameError: name 'var' is not defined when you wrap it all into a function is the following: locals().items() refers to the locally defined variables. as a matter of fact, the locally defined variables in your functions are only the variables defined inside the function and those passed as arguments to the function.

As the name says, locals().items() is local, and will consist of different variables depending on where it is called in your script.

On the contrary globals().items() consists of all the global variables, so try using it instead in your functions, it should do the trick.

For more info, look up global and local variables as well as the scope of variables in Python.