Getting the original name of an argument

Question:

Task

I would like to have a way to access the original name of a passed argument. I looked at the answers of

which seemed a bit complicated to me. Another idea is to write

import pandas as pd
my_var = pd.DataFrame(...)
def f(dataset: pd.DataFrame):
    for name, obj in globals().items():
        if id(obj) == id(dataset):
            return name
f(my_var)

which returns ‘my_var’. This makes sense to me and does not seem to be brittle.

However, since I did not see such an answer anywhere, I am wondering if I am missing something and this answer is actually a bad idea.

Questions

  1. Is this code a good/valid idea?
  2. If not why is a different (which?) answer better?

What am I NOT asking

I am NOT asking how to "how to get the original variable name of variable passed to a function". I am asking whether/why my suggested code is problematic. This is a different question.

Background

I want to use this as a helper function in a data analytics task, where I use the variable’s name as a label for the later plot.

import pandas as pd

def f(dataset: pd.DataFrame):
    return_dataframe = do_stuff(dataset)
    for name, obj in globals().items():
        if id(obj) == id(dataset):
            return_dataframe["group"] = name
            return return_dataframe

data_frame_for_plotting = pd.concat([f(data_train), f(data_test)])
Asked By: Make42

||

Answers:

Variable names aren’t data. Don’t use variable names as data. Labels are data. Explicitly pass data as strings. Variable names are subject to change as you refactor your code, sometimes for technical necessities, sometimes for readability. Don’t make the outcome of your program dependent on the names of the variables.


Having said that, your function works iff:

  • You take care to never assign data frames to two or more variables for any reason; i.e. foo = bar will already break it.
  • Your variables must be globals in the same module, i.e. you cannot wrap your code into functions or separate it into modules.
  • You’re aware that this ties the outcome of your program to your variable names, and when refactoring your code for readability or other reasons your output may also change.

If that works for you in your specific situation… well… go for it. But generally speaking this is terrifically brittle and restricts your coding freedom. Is that worth it to save a few keystrokes?

Answered By: deceze

First off, I agree with @deceze: this is not something that is done. But if you still want to use it (as in, rapid prototyping and you double-pinky-promise to get rid of it before anyone else sees it), this removes some of the downsides of your approach: it will work as long as the argument is a variable; it does not depend on the value, and does not care about whether the variable is global or not. On the flip side, you have to pip install executing.

import inspect
import executing
import ast

def getArgName(argNo):
    thisFrame = inspect.currentframe()
    funcFrame = thisFrame.f_back
    callerFrame = funcFrame.f_back
    callNode = executing.Source.executing(callerFrame).node
    args = callNode.args
    arg = args[argNo]
    assert isinstance(arg, ast.Name), f"The first argument to {funcFrame.f_code.co_name} has to be a variable"
    return arg.id

def seek(what):
    name_of_what = getArgName(0)
    print(f"{name_of_what} is {what}")

answer = 42
seek(answer)
# => answer is 42

However… Even though it fixes some of the downsides of the naive solution, I have to reiterate that this is still not how Python is supposed to be used. Python people generally hate hacks like these.

Answered By: Amadan
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.