This question already has answers here:
Getting the name of a variable as a string
(32 answers)
Closed 4 months ago.
Is it possible to get the original variable name of a variable passed to a function? E.g.
foobar = "foo"
def func(var):
print var.origname
So that:
func(foobar)
Returns:
>>foobar
EDIT:
All I was trying to do was make a function like:
def log(soup):
f = open(varname+'.html', 'w')
print >>f, soup.prettify()
f.close()
.. and have the function generate the filename from the name of the variable passed to it.
I suppose if it's not possible I'll just have to pass the variable and the variable's name as a string each time.
EDIT: To make it clear, I don't recommend using this AT ALL, it will break, it's a mess, it won't help you in any way, but it's doable for entertainment/education purposes.
You can hack around with the inspect module, I don't recommend that, but you can do it...
import inspect
def foo(a, f, b):
frame = inspect.currentframe()
frame = inspect.getouterframes(frame)[1]
string = inspect.getframeinfo(frame[0]).code_context[0].strip()
args = string[string.find('(') + 1:-1].split(',')
names = []
for i in args:
if i.find('=') != -1:
names.append(i.split('=')[1].strip())
else:
names.append(i)
print names
def main():
e = 1
c = 2
foo(e, 1000, b = c)
main()
Output:
['e', '1000', 'c']
To add to Michael Mrozek's answer, you can extract the exact parameters versus the full code by:
import re
import traceback
def func(var):
stack = traceback.extract_stack()
filename, lineno, function_name, code = stack[-2]
vars_name = re.compile(r'\((.*?)\).*$').search(code).groups()[0]
print vars_name
return
foobar = "foo"
func(foobar)
# PRINTS: foobar
Looks like Ivo beat me to inspect, but here's another implementation:
import inspect
def varName(var):
lcls = inspect.stack()[2][0].f_locals
for name in lcls:
if id(var) == id(lcls[name]):
return name
return None
def foo(x=None):
lcl='not me'
return varName(x)
def bar():
lcl = 'hi'
return foo(lcl)
bar()
# 'lcl'
Of course, it can be fooled:
def baz():
lcl = 'hi'
x='hi'
return foo(lcl)
baz()
# 'x'
Moral: don't do it.
Another way you can try if you know what the calling code will look like is to use traceback:
def func(var):
stack = traceback.extract_stack()
filename, lineno, function_name, code = stack[-2]
code will contain the line of code that was used to call func (in your example, it would be the string func(foobar)). You can parse that to pull out the argument
You can't. It's evaluated before being passed to the function. All you can do is pass it as a string.
#Ivo Wetzel's answer works in the case of function call are made in one line, like
e = 1 + 7
c = 3
foo(e, 100, b=c)
In case that function call is not in one line, like:
e = 1 + 7
c = 3
foo(e,
1000,
b = c)
below code works:
import inspect, ast
def foo(a, f, b):
frame = inspect.currentframe()
frame = inspect.getouterframes(frame)[1]
string = inspect.findsource(frame[0])[0]
nodes = ast.parse(''.join(string))
i_expr = -1
for (i, node) in enumerate(nodes.body):
if hasattr(node, 'value') and isinstance(node.value, ast.Call)
and hasattr(node.value.func, 'id') and node.value.func.id == 'foo' # Here goes name of the function:
i_expr = i
break
i_expr_next = min(i_expr + 1, len(nodes.body)-1)
lineno_start = nodes.body[i_expr].lineno
lineno_end = nodes.body[i_expr_next].lineno if i_expr_next != i_expr else len(string)
str_func_call = ''.join([i.strip() for i in string[lineno_start - 1: lineno_end]])
params = str_func_call[str_func_call.find('(') + 1:-1].split(',')
print(params)
You will get:
[u'e', u'1000', u'b = c']
But still, this might break.
You can use python-varname package
from varname import nameof
s = 'Hey!'
print (nameof(s))
Output:
s
Package below:
https://github.com/pwwang/python-varname
For posterity, here's some code I wrote for this task, in general I think there is a missing module in Python to give everyone nice and robust inspection of the caller environment. Similar to what rlang eval framework provides for R.
import re, inspect, ast
#Convoluted frame stack walk and source scrape to get what the calling statement to a function looked like.
#Specifically return the name of the variable passed as parameter found at position pos in the parameter list.
def _caller_param_name(pos):
#The parameter name to return
param = None
#Get the frame object for this function call
thisframe = inspect.currentframe()
try:
#Get the parent calling frames details
frames = inspect.getouterframes(thisframe)
#Function this function was just called from that we wish to find the calling parameter name for
function = frames[1][3]
#Get all the details of where the calling statement was
frame,filename,line_number,function_name,source,source_index = frames[2]
#Read in the source file in the parent calling frame upto where the call was made
with open(filename) as source_file:
head=[source_file.next() for x in xrange(line_number)]
source_file.close()
#Build all lines of the calling statement, this deals with when a function is called with parameters listed on each line
lines = []
#Compile a regex for matching the start of the function being called
regex = re.compile(r'\.?\s*%s\s*\(' % (function))
#Work backwards from the parent calling frame line number until we see the start of the calling statement (usually the same line!!!)
for line in reversed(head):
lines.append(line.strip())
if re.search(regex, line):
break
#Put the lines we have groked back into sourcefile order rather than reverse order
lines.reverse()
#Join all the lines that were part of the calling statement
call = "".join(lines)
#Grab the parameter list from the calling statement for the function we were called from
match = re.search('\.?\s*%s\s*\((.*)\)' % (function), call)
paramlist = match.group(1)
#If the function was called with no parameters raise an exception
if paramlist == "":
raise LookupError("Function called with no parameters.")
#Use the Python abstract syntax tree parser to create a parsed form of the function parameter list 'Name' nodes are variable names
parameter = ast.parse(paramlist).body[0].value
#If there were multiple parameters get the positional requested
if type(parameter).__name__ == 'Tuple':
#If we asked for a parameter outside of what was passed complain
if pos >= len(parameter.elts):
raise LookupError("The function call did not have a parameter at postion %s" % pos)
parameter = parameter.elts[pos]
#If there was only a single parameter and another was requested raise an exception
elif pos != 0:
raise LookupError("There was only a single calling parameter found. Parameter indices start at 0.")
#If the parameter was the name of a variable we can use it otherwise pass back None
if type(parameter).__name__ == 'Name':
param = parameter.id
finally:
#Remove the frame reference to prevent cyclic references screwing the garbage collector
del thisframe
#Return the parameter name we found
return param
If you want a Key Value Pair relationship, maybe using a Dictionary would be better?
...or if you're trying to create some auto-documentation from your code, perhaps something like Doxygen (http://www.doxygen.nl/) could do the job for you?
I wondered how IceCream solves this problem. So I looked into the source code and came up with the following (slightly simplified) solution. It might not be 100% bullet-proof (e.g. I dropped get_text_with_indentation and I assume exactly one function argument), but it works well for different test cases. It does not need to parse source code itself, so it should be more robust and simpler than previous solutions.
#!/usr/bin/env python3
import inspect
from executing import Source
def func(var):
callFrame = inspect.currentframe().f_back
callNode = Source.executing(callFrame).node
source = Source.for_frame(callFrame)
expression = source.asttokens().get_text(callNode.args[0])
print(expression, '=', var)
i = 1
f = 2.0
dct = {'key': 'value'}
obj = type('', (), {'value': 42})
func(i)
func(f)
func(s)
func(dct['key'])
func(obj.value)
Output:
i = 1
f = 2.0
s = string
dct['key'] = value
obj.value = 42
Update: If you want to move the "magic" into a separate function, you simply have to go one frame further back with an additional f_back.
def get_name_of_argument():
callFrame = inspect.currentframe().f_back.f_back
callNode = Source.executing(callFrame).node
source = Source.for_frame(callFrame)
return source.asttokens().get_text(callNode.args[0])
def func(var):
print(get_name_of_argument(), '=', var)
If you want to get the caller params as in #Matt Oates answer answer without using the source file (ie from Jupyter Notebook), this code (combined from #Aeon answer) will do the trick (at least in some simple cases):
def get_caller_params():
# get the frame object for this function call
thisframe = inspect.currentframe()
# get the parent calling frames details
frames = inspect.getouterframes(thisframe)
# frame 0 is the frame of this function
# frame 1 is the frame of the caller function (the one we want to inspect)
# frame 2 is the frame of the code that calls the caller
caller_function_name = frames[1][3]
code_that_calls_caller = inspect.findsource(frames[2][0])[0]
# parse code to get nodes of abstract syntact tree of the call
nodes = ast.parse(''.join(code_that_calls_caller))
# find the node that calls the function
i_expr = -1
for (i, node) in enumerate(nodes.body):
if _node_is_our_function_call(node, caller_function_name):
i_expr = i
break
# line with the call start
idx_start = nodes.body[i_expr].lineno - 1
# line with the end of the call
if i_expr < len(nodes.body) - 1:
# next expression marks the end of the call
idx_end = nodes.body[i_expr + 1].lineno - 1
else:
# end of the source marks the end of the call
idx_end = len(code_that_calls_caller)
call_lines = code_that_calls_caller[idx_start:idx_end]
str_func_call = ''.join([line.strip() for line in call_lines])
str_call_params = str_func_call[str_func_call.find('(') + 1:-1]
params = [p.strip() for p in str_call_params.split(',')]
return params
def _node_is_our_function_call(node, our_function_name):
node_is_call = hasattr(node, 'value') and isinstance(node.value, ast.Call)
if not node_is_call:
return False
function_name_correct = hasattr(node.value.func, 'id') and node.value.func.id == our_function_name
return function_name_correct
You can then run it as this:
def test(*par_values):
par_names = get_caller_params()
for name, val in zip(par_names, par_values):
print(name, val)
a = 1
b = 2
string = 'text'
test(a, b,
string
)
to get the desired output:
a 1
b 2
string text
Since you can have multiple variables with the same content, instead of passing the variable (content), it might be safer (and will be simpler) to pass it's name in a string and get the variable content from the locals dictionary in the callers stack frame. :
def displayvar(name):
import sys
return name+" = "+repr(sys._getframe(1).f_locals[name])
If it just so happens that the variable is a callable (function), it will have a __name__ property.
E.g. a wrapper to log the execution time of a function:
def time_it(func, *args, **kwargs):
start = perf_counter()
result = func(*args, **kwargs)
duration = perf_counter() - start
print(f'{func.__name__} ran in {duration * 1000}ms')
return result
here's a newb Python question that I'm sure is a no-brainer for the pros. I have an object OddStream that is called from the function print_from_stream to create a list of odd numbers starting from 1. It works find the first time but when calling it a second time the new list starts from the last number of the first list + 2.
What should I write to reset OddStream? BTW, this is only a portion of the code, but it's the part that matters. Thanks if you can help.
class OddStream(object):
def __init__(self):
self.current = 1
def get_next(self):
to_return = self.current
self.current += 2
return to_return
def print_from_stream(n):
for _ in range(n):
print(stream.get_next())
I guess the stream instance of the OddStream class is being instantiated like this:
class OddStream(object):
# ...
stream = OddStream() # Breakpoint 1
def print_from_stream(n):
for _ in range(n):
print(stream.get_next()) # Breakpoint 2
At breakpoint 1 stream has been instantiated and stream.current = 1
At breakpoint 2 stream.get_next() has been called and it has increased stream.current
So the behaviour you described should be expected.
To avoid such behaviour, add a reset() method to OddStream and call it from print_from_stream():
class OddStream(object):
# ...
def reset(self):
self.current = 1
stream = OddStream()
def print_from_stream(n):
stream.reset()
for _ in range(n):
print(stream.get_next())
print("First run:")
print_from_stream(10)
print("Second run:")
print_from_stream(10)
I have a function on Python get_object_by_id, that restores object by its id, calling different functions depending on the type of the object:
def get_object_by_id(object_id: int) -> tp.Union[int, float, tuple, list, str, bool]:
"""
Restores object by id.
:param object_id: Object Id.
:return: An object that corresponds to object_id.
"""
info = struct.unpack("LL", ctypes.string_at(object_id, 16))
if info[1] == id(int):
return get_int_by_id(object_id)
elif info[1] == id(float):
return get_float_by_id(object_id)
elif info[1] == id(bool):
return get_bool_by_id(object_id)
elif info[1] == id(str):
return get_str_by_id(object_id)
elif info[1] == id(list):
return get_list_by_id(object_id)
elif info[1] == id(tuple):
return get_tuple_by_id(object_id)
else:
return None
My function get_list_by_id restores lists recursively:
def get_list_by_id(object_id: int) -> list:
info = struct.unpack("5L", ctypes.string_at(object_id, 40))
size_of_list = str(info[2]) + 'L'
elements_ides = struct.unpack(size_of_list, ctypes.string_at(info[3], 8 * info[2]))
res_list = []
for i in range(info[2]):
res_list.append(get_object_by_id(elements_ides[i]))
return res_list
It works well if nested lists are not very deep, but exceeds maximum recursion depth otherwise. I'm new to Python, and I struggle to understand, how can I rewrite this function without recursion and not make it looking monstrous.
Well, I didn't test it, so I don't know if it really works, but hopefully it makes the point: if the system stack won't do it, make your own stack.
def get_list_by_id(object_id: int) -> list:
stack_im_working_on = [] # maybe dqueue is faster?
# records are [info, elements_ides, 2=res_list, 3=i]
def push_new_list(object_id: int) -> list:
info = struct.unpack("5L", ctypes.string_at(object_id, 40))
size_of_list = str(info[2]) + 'L'
elements_ides = struct.unpack(size_of_list, ctypes.string_at(info[3], 8 * info[2]))
res_list = []
nonlocal stack_im_working_on
stack_im_working_on.append([info, elements_ides, res_list, 0])
push_new_list(object_id) # start from working on this
while True: # work from top of the stack
info, elements_ides, res_list, i = stack_im_working_on[-1]
while i < info[2]:
if info[1] == id(list): # The recursive call does not happen
push_new_list(elements_ides[i]) # Now we work on this
break # go to while stack_im_working_on
else:
res_list.append(get_object_by_id(elements_ides[i]))
# res_list is shared with the stack frame, but i is not.
stack_im_working_on[-1][3] = i = i + 1 # save that progress
else: # else for while... run when it gets to the end, until NOT i < info[2](and not on break)
# we finished this stack frame, so return the answer
stack_im_working_on.pop() # removes last element (that we just finished)
if stack_im_working_on:
# return to prev stack frame (not done yet)
stack_im_working_on[-1][2].append(res_list)
else:
# return to caller
return res_list
# Not Reached, as the only pop() checks for empty above
I hear that people don't like the "else" at the end of a loop, but it really is useful. (I always put a comment on it)
On the other hand, if your stack really won't ever be that huge, and you can predict a max size... just increase the system stack size limit. (it looks alot easier)
https://stackoverflow.com/questions/3323001/what-is-the-maximum-recursion-depth-in-python-and-how-to-increase-it
import sys
sys.setrecursionlimit(1500)
I have a tree class in which the class gets initialized with a data, left, and right attributes.
in the same class I have a "save" method.
I am using a list as a queue.
I am attempting to create a "save" method which takes only one argument "data".
The purpose of this save method is to dequeue from my list, check that node to see if its empty and if it is then it saves my data there. Otherwise it enqueues the 2 children of that node into the list.
The purpose of this is to save data in level order into the tree.
Because the class gets initialized there is always at least 1 element in the tree which is the root node.
The issue i keep running into is that whenever i append the self.data (the root node, not the data im currently trying to add) into my list at the beginning of the save method it only saves the data there.
and obviously when I then try to append the left and right child of this int i get an error because the int has no left or right attributes.
I am wondering how to save the node in the list instead of the data at the node.
class Tree():
aqueue = []
def __init__(self, item):
self.item = item
self.leftchild = None
self.rightchild = None
self.aqueue.append(self.item)
def add(self, newitem):
temp = self.myqueue.pop(0)
if temp is None:
temp = Tree(newitem)
else:
self.aqueue.append(temp.leftchild)
self.aqueue.append(temp.rightcild)
temp.add(newitem)
self.aqueue.clear() #this is meant to clear queue of all nodes after the recursions are complete
self.aqueue.append(self.item) #this is meant to return the root node to the queue so that it is the only item for next time
There are a couple of obvious issues with your code: both the if and else branch return, so the code after will never run, temp == newitem is an equality expression, but even if it was an assignment it wouldn't do anything:
def add(self, newitem):
temp = self.myqueue.pop(0)
if temp == None: # should use temp is None
temp == newitem # temp = newitem still wouldn't do anything
return True
else:
self.aqueue.append(temp.leftchild)
self.aqueue.append(temp.rightcild)
return temp.add(newitem)
# you will never get here, since both branches of the if returns
self.aqueue.clear() # delete everything in the list..?
self.aqueue.append(self.item)
I have been given a class which implements a Priority Queue , using a function to evaluate the priority.
class PriorityQueueWithFunction(PriorityQueue):
"""
Implements a priority queue with the same push/pop signature of the
Queue and the Stack classes. This is designed for drop-in replacement for
those two classes. The caller has to provide a priority function, which
extracts each item's priority.
"""
def __init__(self, priorityFunction):
# type: (object) -> object
"priorityFunction (item) -> priority"
self.priorityFunction = priorityFunction # store the priority function
PriorityQueue.__init__(self) # super-class initializer
def push(self, item):
"Adds an item to the queue with priority from the priority function"
PriorityQueue.push(self, item, self.priorityFunction(item))
I have been also , given , the priority function that I am going to initialize the class above with.
def manhattanHeuristic(position, problem, info={}):
"The Manhattan distance heuristic for a PositionSearchProblem"
xy1 = position
xy2 = problem.goal
return abs(xy1[0] - xy2[0]) + abs(xy1[1] - xy2[1])
The above code is given to us and we cannot change it. I must create that PriorityQueueWithFunction Class and push an element to it. The push function of my class takes on argument , the item. But my PriorityFunction takes 2.
What kind of arguments should i use to push the right elemnt into my class and also make my priorityfunction work properly ?
That's what i tried and i am getting compiling errors , manhattanHeuristic...takes 2 arguments , 1 given
#Creating a queingFn
queuingFn = PriorityQueueWithFunction(heuristic)
Frontier = queuingFn
#Creating the item that needs to be pushed
StartState = problem.getStartState()
StartNode = (StartState,'',0,(-1,-1))
#Here is my problem
item = StartState , problem
Frontier.push(item)
Should I change my item's form ? Any ideas ?
You should make a new method that wraps call to manhattanHeuristic:
# for item as dict: item = {'position': POS, 'problem': PROBLEM}
def oneArgHeuristic(item):
position = item.position
problem = item.problem
return manhattanHeuristic(position, problem)
# for item as tuple: item = (POS, PROBLEM)
def oneArgHeuristic(item):
position, problem = item
return manhattanHeuristic(position, problem)
and pass it to PriorityQueueWithFunction instead of the original one