This question already has answers here:
How to print original variable's name in Python after it was returned from a function?
(13 answers)
Closed 4 years ago.
Say I have a variable named choice it is equal to 2. How would I access the name of the variable? Something equivalent to
In [53]: namestr(choice)
Out[53]: 'choice'
for use in making a dictionary. There's a good way to do this and I'm just missing it.
EDIT:
The reason to do this is thus. I am running some data analysis stuff where I call the program with multiple parameters that I would like to tweak, or not tweak, at runtime. I read in the parameters I used in the last run from a .config file formated as
filename
no_sig_resonance.dat
mass_peak
700
choice
1,2,3
When prompted for values, the previously used is displayed and an empty string input will use the previously used value.
My question comes about because when it comes to writing the dictionary that these values have been scanned into. If a parameter is needed I run get_param which accesses the file and finds the parameter.
I think I will avoid the problem all together by reading the .config file once and producing a dictionary from that. I avoided that originally for... reasons I no longer remember. Perfect situation to update my code!
If you insist, here is some horrible inspect-based solution.
import inspect, re
def varname(p):
for line in inspect.getframeinfo(inspect.currentframe().f_back)[3]:
m = re.search(r'\bvarname\s*\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)', line)
if m:
return m.group(1)
if __name__ == '__main__':
spam = 42
print varname(spam)
I hope it will inspire you to reevaluate the problem you have and look for another approach.
To answer your original question:
def namestr(obj, namespace):
return [name for name in namespace if namespace[name] is obj]
Example:
>>> a = 'some var'
>>> namestr(a, globals())
['a']
As #rbright already pointed out whatever you do there are probably better ways to do it.
If you are trying to do this, it means you are doing something wrong. Consider using a dict instead.
def show_val(vals, name):
print "Name:", name, "val:", vals[name]
vals = {'a': 1, 'b': 2}
show_val(vals, 'b')
Output:
Name: b val: 2
Rather than ask for details to a specific solution, I recommend describing the problem you face; I think you'll get better answers. I say this since there's almost certainly a better way to do whatever it is you're trying to do. Accessing variable names in this way is not commonly needed to solve problems in any language.
That said, all of your variable names are already in dictionaries which are accessible through the built-in functions locals and globals. Use the correct one for the scope you are inspecting.
One of the few common idioms for inspecting these dictionaries is for easy string interpolation:
>>> first = 'John'
>>> last = 'Doe'
>>> print '%(first)s %(last)s' % globals()
John Doe
This sort of thing tends to be a bit more readable than the alternatives even though it requires inspecting variables by name.
You can't, as there are no variables in Python but only names.
For example:
> a = [1,2,3]
> b = a
> a is b
True
Which of those two is now the correct variable? There's no difference between a and b.
There's been a similar question before.
Will something like this work for you?
>>> def namestr(**kwargs):
... for k,v in kwargs.items():
... print "%s = %s" % (k, repr(v))
...
>>> namestr(a=1, b=2)
a = 1
b = 2
And in your example:
>>> choice = {'key': 24; 'data': None}
>>> namestr(choice=choice)
choice = {'data': None, 'key': 24}
>>> printvars(**globals())
__builtins__ = <module '__builtin__' (built-in)>
__name__ = '__main__'
__doc__ = None
namestr = <function namestr at 0xb7d8ec34>
choice = {'data': None, 'key': 24}
With eager evaluation, variables essentially turn into their values any time you look at them (to paraphrase). That said, Python does have built-in namespaces. For example, locals() will return a dictionary mapping a function's variables' names to their values, and globals() does the same for a module. Thus:
for name, value in globals().items():
if value is unknown_variable:
... do something with name
Note that you don't need to import anything to be able to access locals() and globals().
Also, if there are multiple aliases for a value, iterating through a namespace only finds the first one.
For the revised question of how to read in configuration parameters, I'd strongly recommend saving yourself some time and effort and use ConfigParser or (my preferred tool) ConfigObj.
They can do everything you need, they're easy to use, and someone else has already worried about how to get them to work properly!
Related
I have found some vaguely related questions to this question, but not any clean and specific solution for CPython. And I assume that a "valid" solution is interpreter specific.
First the things I think I understand:
locals() gives a non-modifiable dictionary.
A function may (and indeed does) use some kind of optimization to access its local variables
frame.f_locals gives a locals() like dictionary, but less prone to hackish things through exec. Or at least I have been less able to do hackish undocumented things like the locals()['var'] = value ; exec ""
exec is capable to do weird things to the local variables, but it is not reliable --e.g. I read somewhere that it doesn't work in Python 3. Haven't tested.
So I understand that, given those limitations, it will never be safe to add extra variables to the locals, because it breaks the interpreter structure.
However, it should be possible to change a variable already existing, isn't it?
Things that I considered
In a function f, one can access the f.func_code.co_nlocals and f.func_code.co_varnames.
In a frame, the variables can be accessed / checked / read through the frame.f_locals. This is in the use case of setting a tracer through sys.settrace.
One can easily access the function in which a frame is --cosidering the use case of setting a trace and using it to "do things" in with the local variables given a certain trigger or whatever.
The variables should be somewhere, preferably writeable... but I am not capable of finding it. Even if it is an array (for interpreter efficient access), or I need some extra C-specific wiring, I am ready to commit to it.
How can I achieve that modification of variables from a tracer function or from a decorated wrapped function or something like that?
A full solution will be of course appreciated, but even some pointers will help me greatly, because I'm stuck here with lots of non writeable dictionaries :-/
Edit: Hackish exec is doing things like this or this
It exists an undocumented C-API call for doing things like that:
PyFrame_LocalsToFast
There is some more discussion in this PyDev blog post. The basic idea seems to be:
import ctypes
...
frame.f_locals.update({
'a': 'newvalue',
'b': other_local_value,
})
ctypes.pythonapi.PyFrame_LocalsToFast(
ctypes.py_object(frame), ctypes.c_int(0))
I have yet to test if this works as expected.
Note that there might be some way to access the Fast directly, to avoid an indirection if the requirements is only modification of existing variable. But, as this seems to be mostly non-documented API, source code is the documentation resource.
Based on the notes from MariusSiuram, I wrote a recipe that show the behavior.
The conclusions are:
we can modify an existing variable
we can delete an existing variable
we can NOT add a new variable.
So, here is the code:
import inspect
import ctypes
def parent():
a = 1
z = 'foo'
print('- Trying to add a new variable ---------------')
hack(case=0) # just try to add a new variable 'b'
print(a)
print(z)
assert a == 1
assert z == 'foo'
try:
print (b)
assert False # never is going to reach this point
except NameError, why:
print("ok, global name 'b' is not defined")
print('- Trying to remove an existing variable ------')
hack(case=1)
print(a)
assert a == 2
try:
print (z)
except NameError, why:
print("ok, we've removed the 'z' var")
print('- Trying to update an existing variable ------')
hack(case=2)
print(a)
assert a == 3
def hack(case=0):
frame = inspect.stack()[1][0]
if case == 0:
frame.f_locals['b'] = "don't work"
elif case == 1:
frame.f_locals.pop('z')
frame.f_locals['a'] += 1
else:
frame.f_locals['a'] += 1
# passing c_int(1) will remove and update variables as well
# passing c_int(0) will only update
ctypes.pythonapi.PyFrame_LocalsToFast(
ctypes.py_object(frame),
ctypes.c_int(1))
if __name__ == '__main__':
parent()
The output would be like:
- Trying to add a new variable ---------------
1
foo
ok, global name 'b' is not defined
- Trying to remove an existing variable ------
2
foo
- Trying to update an existing variable ------
3
[EDIT 00]: I've edited several times the post and now even the title, please read below.
I just learned about the format string method, and its use with dictionaries, like the ones provided by vars(), locals() and globals(), example:
name = 'Ismael'
print 'My name is {name}.'.format(**vars())
But I want to do:
name = 'Ismael'
print 'My name is {name}.' # Similar to ruby
So I came up with this:
def mprint(string='', dictionary=globals()):
print string.format(**dictionary)
You can interact with the code here:
http://labs.codecademy.com/BA0B/3#:workspace
Finally, what I would love to do is to have the function in another file, named my_print.py, so I could do:
from my_print import mprint
name= 'Ismael'
mprint('Hello! My name is {name}.')
But as it is right now, there is a problem with the scopes, how could I get the the main module namespace as a dictionary from inside the imported mprint function. (not the one from my_print.py)
I hope I made myself uderstood, if not, try importing the function from another module. (the traceback is in the link)
It's accessing the globals() dict from my_print.py, but of course the variable name is not defined in that scope, any ideas of how to accomplish this?
The function works if it's defined in the same module, but notice how I must use globals() because if not I would only get a dictionary with the values within mprint() scope.
I have tried using nonlocal and dot notation to access the main module variables, but I still can't figure it out.
[EDIT 01]: I think I've figured out a solution:
In my_print.py:
def mprint(string='',dictionary=None):
if dictionary is None:
import sys
caller = sys._getframe(1)
dictionary = caller.f_locals
print string.format(**dictionary)
In test.py:
from my_print import mprint
name = 'Ismael'
country = 'Mexico'
languages = ['English', 'Spanish']
mprint("Hello! My name is {name}, I'm from {country}\n"
"and I can speak {languages[1]} and {languages[0]}.")
It prints:
Hello! My name is Ismael, I'm from Mexico
and I can speak Spanish and English.
What do you think guys? That was a difficult one for me!
I like it, much more readable for me.
[EDIT 02]: I've made a module with an interpolate function, an Interpolate class and an attempt for a interpolate class method analogous to the function.
It has a small test suite and its documented!
I'm stuck with the method implementation, I don't get it.
Here's the code: http://pastebin.com/N2WubRSB
What do you think guys?
[EDIT 03]: Ok I have settled with just the interpolate() function for now.
In string_interpolation.py:
import sys
def get_scope(scope):
scope = scope.lower()
caller = sys._getframe(2)
options = ['l', 'local', 'g', 'global']
if scope not in options[:2]:
if scope in options[2:]:
return caller.f_globals
else:
raise ValueError('invalid mode: {0}'.format(scope))
return caller.f_locals
def interpolate(format_string=str(),sequence=None,scope='local',returns=False):
if type(sequence) is str:
scope = sequence
sequence = get_scope(scope)
else:
if not sequence:
sequence = get_scope(scope)
format = 'format_string.format(**sequence)'
if returns is False:
print eval(format)
elif returns is True:
return eval(format)
Thanks again guys! Any opinions?
[EDIT 04]:
This is my last version, it has a test, docstrings and describes some limitations I've found:
http://pastebin.com/ssqbbs57
You can quickly test the code here:
http://labs.codecademy.com/BBMF#:workspace
And clone grom git repo here:
https://github.com/Ismael-VC/python_string_interpolation.git
Modules don't share namespaces in python, so globals() for my_print is always going to be the globals() of my_print.py file ; i.e the location where the function was actually defined.
def mprint(string='', dic = None):
dictionary = dic if dic is not None else globals()
print string.format(**dictionary)
You should pass the current module's globals() explicitly to make it work.
Ans don't use mutable objects as default values in python functions, it can result in unexpected results. Use None as default value instead.
A simple example for understanding scopes in modules:
file : my_print.py
x = 10
def func():
global x
x += 1
print x
file : main.py
from my_print import *
x = 50
func() #prints 11 because for func() global scope is still
#the global scope of my_print file
print x #prints 50
Part of your problem - well, the reason its not working - is highlighted in this question.
You can have your function work by passing in globals() as your second argument, mprint('Hello my name is {name}',globals()).
Although it may be convenient in Ruby, I would encourage you not to write Ruby in Python if you want to make the most out of the language.
Language Design Is Not Just Solving Puzzles: ;)
http://www.artima.com/forums/flat.jsp?forum=106&thread=147358
Edit: PEP-0498 solves this issue!
The Template class from the string module, also does what I need (but more similar to the string format method), in the end it also has the readability I seek, it also has the recommended explicitness, it's in the Standard Library and it can also be easily customized and extended.
http://docs.python.org/2/library/string.html?highlight=template#string.Template
from string import Template
name = 'Renata'
place = 'hospital'
job = 'Dr.'
how = 'glad'
header = '\nTo Ms. {name}:'
letter = Template("""
Hello Ms. $name.
I'm glad to inform, you've been
accepted in our $place, and $job Red
will ${how}ly recieve you tomorrow morning.
""")
print header.format(**vars())
print letter.substitute(vars())
The funny thing is that now I'm getting more fond of using {} instead of $ and I still like the string_interpolation module I came up with, because it's less typing than either one in the long run. LOL!
Run the code here:
http://labs.codecademy.com/BE3n/3#:workspace
I want a function that can return the variable/object name as str like this :
def get_variable_name (input_variable):
## some codes
>>get_variable_name(a)
'a'
>>get_variable_name(mylist)
'mylist'
it looks like silly but i need the function to construct expression regarding to the variable for later on 'exec()'. Can someone help on how to write the 'get_variable_name' ?
I've seen a few variants on this kind of question several times on SO now. The answer is don't. Learn to use a dict anytime you need association between names and objects. You will thank yourself for this later.
In answer to the question "How can my code discover the name of an object?", here's a quote from Fredrik Lundh (on comp.lang.python):
The same way as you get the name of that cat you found on your porch:
the cat (object) itself cannot tell you its name, and it doesn’t
really care — so the only way to find out what it’s called is to ask
all your neighbours (namespaces) if it’s their cat (object)…
….and don’t be surprised if you’ll find that it’s known by many names,
or no name at all!
Note: It is technically possible to get a list of the names which are bound to an object, at least in CPython implementation. If you're interested to see that demonstrated, see the usage of the inspect module shown in my answer here:
Can an object inspect the name of the variable it's been assigned to?
This technique should only be used in some crazy debugging session, don't use anything like this in your design.
In general it is not possible. When you pass something to a function, you are passing the object, not the name. The same object can have many names or no names. What is the function supposed to do if you call get_variable_name(37)? You should think about why you want to do this, and try to find another way to accomplish your real task.
Edit: If you want get_variable_name(37) to return 37, then if you do a=37 and then do get_variable_name(a), that will also return 37. Once inside the function, it has no way of knowing what the object's "name" was outside.
def getvariablename(vara):
for k in globals():
if globals()[k] == vara:
return k
return str(vara)
may work in some instance ...but very subject to breakage... and I would basically never use it in any kind of production code...
basically I cant think of any good reason to do this ... and about a million not to
Here's a good start, depending on the Python version and runtime you might have to tweak a little. Put a break point and spend sometime to understand the structure of inspect.currentframe()
import inspect
def vprint(v):
v_name = inspect.currentframe().f_back.f_code.co_names[3]
print(f"{v_name} ==> {v}")
if __name__ == '__main__':
x = 15
vprint(x)
will produce
x ==> 15
if you just want to return the name of a variable selected based on user input... so they can keep track of their input, add a variable name in the code as they make selections in addition to the values generated from their selections. for example:
temp = raw_input('Do you want a hot drink? Type yes or no. ')
size = raw_input('Do you want a large drink? Type yes or no. ')
if temp and size == 'yes':
drink = HL
name = 'Large cafe au lait'
if temp and size != 'yes':
drink = CS
name = 'Small ice coffee'
print 'You ordered a ', name, '.'
MJ
If your statement to be used in exec() is something like this
a = ["ddd","dfd","444"]
then do something like this
exec('b = a = ["ddd","dfd","444"]')
now you can use 'b' in your code to get a handle on 'a'.
Perhaps you can use traceback.extract_stack() to get the call stack, then extract the variable name(s) from the entry?
def getVarName(a):
stack = extract_stack()
print(stack.pop(-2)[3])
bob = 5
getVarName(bob);
Output:
getVarName(bob)
I have 5 objects, mac5_le(), mac4_le and so on. I am trying to extract some value from each of the objects as follows,
for i in range(5,-1,-1):
m = locals()['self.mac'+str(i)+'_le.text()']
print m
I am getting the error as KeyError: 'self.mac5_le.text()'.
Any Idea?
What the what?
m = getattr(self, 'mac%d_le' % i).text()
Not sure why you would want to munge objects around that way, but you've definitely got your syntax wrong:
locals()['self.mac'+str(i)+'_le'].text()
should "work".
I see a few things wrong with what you're attempting. First, self.name variables are not local scope. They're either part of the instance, or part of the class. Locals are variables that are accessible from your current function scope, but not the global scope. For instance, in the code below, you would see foo and bar, but not baz:
baz = 1
def silly():
# These two variables are local
foo = 2
bar = 3
print locals()
Calling silly():
>>> silly()
{'foo': 2, 'bar': 3}
Secondly, the locals() and globals() dictionaries don't resolve the dot operator, nor will they call functions.
What you want to do is use something like getattr, or create an api that works better for you (the code you're trying to write isn't very idiomatic Python). Here's what is might look like with getattr:
for i in range(5,-1,-1):
m = getattr(self, 'mac'+str(i)+'_le').text()
print m
getattr is will do all the right lookups underneath the hood to find macN_le. Once you have a reference to the object, then you can call .text() on it.
Hope that helps!
I want to have a function in a different module, that when called, has access to all variables that its caller has access to, and functions just as if its body had been pasted into the caller rather than having its own context, basically like a C Macro instead of a normal function. I know I can pass locals() into the function and then it can access the local variables as a dict, but I want to be able to access them normally (eg x.y, not x["y"] and I want all names the caller has access to not just the locals, as well as things that were 'imported' into the caller's file but not into the module that contains the function.
Is this possible to pull off?
Edit 2 Here's the simplest possible example I can come up with of what I'm really trying to do:
def getObj(expression)
ofs = expression.rfind(".")
obj = eval(expression[:ofs])
print "The part of the expression Left of the period is of type ", type(obj),
Problem is that 'expression' requires the imports and local variables of the caller in order to eval without error.In reality theres a lot more than just an eval, so I'm trying to avoid the solution of just passing locals() in and through to the eval() since that won't fix my general case problem.
And another, even uglier way to do it -- please don't do this, even if it's possible --
import sys
def insp():
l = sys._getframe(1).f_locals
expression = l["expression"]
ofs = expression.rfind(".")
expofs = expression[:ofs]
obj = eval(expofs, globals(), l)
print "The part of the expression %r Left of the period (%r) is of type %r" % (expression, expofs, type(obj)),
def foo():
derp = 5
expression = "derp.durr"
insp()
foo()
outputs
The part of the expression 'derp.durr' Left of the period ('derp') is of type (type 'int')
I don't presume this is the answer that you wanted to hear, but trying to access local variables from a caller module's scope is not a good idea. If you normally program in PHP or C, you might be used to this sort of thing?
If you still want to do this, you might consider creating a class and passing an instance of that class in place of locals():
#other_module.py
def some_func(lcls):
print(lcls.x)
Then,
>>> import other_module
>>>
>>>
>>> x = 'Hello World'
>>>
>>> class MyLocals(object):
... def __init__(self, lcls):
... self.lcls = lcls
... def __getattr__(self, name):
... return self.lcls[name]
...
>>> # Call your function with an instance of this instead.
>>> other_module.some_func(MyLocals(locals()))
'Hello World'
Give it a whirl.
Is this possible to pull off?
Yes (sort of, in a very roundabout way) which I would strongly advise against it in general (more on that later).
Consider:
myfile.py
def func_in_caller():
print "in caller"
import otherfile
globals()["imported_func"] = otherfile.remote_func
imported_func(123, globals())
otherfile.py
def remote_func(x1, extra):
for k,v in extra.iteritems():
globals()[k] = v
print x1
func_in_caller()
This yields (as expected):
123
in caller
What we're doing here is trickery: we just copy every item into another namespace in order to make this work. This can (and will) break very easily and/or lead to hard to find bugs.
There's almost certainly a better way of solving your problem / structuring your code (we need more information in general on what you're trying to achieve).
From The Zen of Python:
2) Explicit is better than implicit.
In other words, pass in the parameter and don't try to get really fancy just because you think it would be easier for you. Writing code is not just about you.