Get the Name of a Local Variable

Get The Name Of A Local Variable

foo = 1
bar = "42"
baz = Hash.new

%w(foo bar baz).each do |vn|
v = eval(vn)
puts "#{vn} = (#{v.class}) #{v}"
end

But this, of course, doesn't help you if you want a method with 1 argument.

Getting name of local variable at runtime in Python

Python doesn't have a portable way to trace frames… but the CPython implementation does: sys._getframe returns you a frame object.

What can you do with a frame object? See the handy chart in the inspect docs for all the fun things it has, but they include the locals() and globals() as seen by the frame, and the code object executed in the frame—which itself includes local names, unbound names, and cells for closures.

But, as others have points out, you don't really need the frame for this; all you need is the locals, and it's much simpler just to pass it to your context manager explicitly.


If you really want to do this:

import contextlib
import sys

@contextlib.contextmanager
def dumping():
f = sys._getframe(2)
fl = f.f_locals.copy()
try:
yield None
finally:
for name, value in f.f_locals.items():
if name not in fl:
print('{} = {}'.format(name, value))

bar = 0
def foo():
global bar
bar = 3
baz = 4
qux = 5
with dumping():
spam = 'eggs'
eggs = 3
bar = 4
baz = 5

foo()

When run, this should print:

eggs = 3
spam = eggs

In other words, the names and values of only the new variables which were declared within the with block—which is, I think, what you wanted, right?


If you want both new and rebound locals, you'd probably want to store something like this:

fl = {(name, id(value)) for name, value in f.f_locals.items()}

Of course you can also rebind nonlocals and globals, so if you care about that, either stash globals as well (but make sure to check for locals is globals for module-level code), or walk the closure.


If you're using CPython 2 (why? for real projects it sometimes makes sense, but to learn how the internals work for fun? and yet, some people to…), the same code will work. There may be slightly different attribute names, but you can guess them by dumping out the dir of a frame and a code. And obviously you want the 2.x print syntax.

It also works in PyPy, at least 2.0b.


If you're wondering how I knew to use _getframe(2)… I didn't. I was pretty sure it would be 1 or 2 frames up, just possibly 3, but which one? So I just did this:

@contextlib.contextmanager
def dumping():
for i in range(4):
f = sys._getframe(i)
print(f.f_code.co_filename, f.f_code.co_firstlineno, f.f_lineno)

0 is of course dumping itself; 1 is the wrapper function in contextlib.contextmanager; 2 is the calling frame; 3 is the module top level. Which is obvious once you think about it, but it wasn't obvious until I knew the answer. :)

Why is it not possible to get local variable names using Reflection?

You have to differentiate between the human-readable text-based form of CLI and the machine-readable compiled form of CLI.

In text CLI, local variables indeed can have names (see §II.15.4.1.3 of ECMA-335, as explained in Damien's answer).

But in the binary form, local variables don't have names. For that, look at §II.23.2.6, where the binary format for a method's local variable signature (list of all its local variables) is specified. And it doesn't contain any mention of variable names:

LocalVarSig

So, if some tool wants to know the original name of a local variable, it has to look into the debugging information contained in the PDB file. If that's not present, there is no way to find out the name.

It is possible to get local variable names programmatically in Java or Kotlin?

Theoretically, it is possible.

You can use a byte code parser like ASM to extract a method's local variable table from the class file of the declaring class. This table contains the names of all local variables and an offset for their validity to identify the declaration order.

Do however note that this attribute is optional. A compiler can omitt adding this debug information.

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 access local and global variable with same name in C

You could cheat and create a pointer to the global i before declaring the local i:

void fun2( void )
{
int *ip = &i; // get address of global i
int i = 50; // local i ”shadows" global i

printf( "local i = %d, global i = %d\n", i, *ip );
}

EDIT

Seeing as this answer got accepted, I must emphasize that you should never write code like this. This is a band-aid around poor programming practice.

Avoid globals where possible, and where not possible use a naming convention that clearly marks them as global and is unlikely to be shadowed (such as prefixing with a g_ or something similar).

I can't tell you how many hours I've wasted chasing down issues that were due to a naming collision like this.

Getting local variable names defined inside a method from outside the method

You can (re)-parse the method and inspect the S-EXPR tree. See below for a proof of concept. You can get hold of the file where the method is defined using Method#source_location and then read that file. There is surely room for improvement to my code but should get you started. It is a fully functional piece of code and only requires the ruby parser gem (https://github.com/whitequark/parser).

require 'parser/current'
node = Parser::CurrentRuby.parse(DATA.read) # DATA is everything that comes after __END__

def find_definition(node, name)
return node if definition_node?(node, name)
if node.respond_to?(:children)
node.children.find do |child|
find_definition(child, name)
end
end
end

def definition_node?(node, name)
return false if !node.respond_to?(:type)
node.type == :def && node.children.first == name
end

def collect_lvasgn(node)
result = []
return result if !node.respond_to?(:children)

node.children.each do |child|
if child.respond_to?(:type) && child.type == :lvasgn
result << child.children.first
else
result += collect_lvasgn(child)
end
end

result
end

definition = find_definition(node, :foo)
puts collect_lvasgn(definition)

__END__

def foo
var = 100
arr = [1,2]
if something
this = 3 * var
end
end

def bar
var = 200
arr = [3, 4]
end

Do you mind telling us WHY you want to find the variables?



Related Topics



Leave a reply



Submit