I'm making a program that makes variables with a base name and variables like their id and series.
I use vars()[str(name+id+serie)] to make them and make it a button using the tkinter module.
When I launch it, it works until it tries to .get() the value from it, saying
keyError (variable name)
I tried to change how it's named, making it int() or moving the .get() here and there but nothing works.
# -*- coding: utf-8 -*
from tkinter import *
import math
import random
fenetre = Tk()
fenetre.geometry("1000x1000")
kanvas=Canvas(fenetre, width=500, height=500, bg="white")
id = 0
serie = 1
idcounter=0
while 1:
print("serie =",serie)
def cheezegrater():
global serie,id,idcounter
vars()[str("var_cheeze_sum"+str(serie))]=0
for o in range(1,val+1):
print("var11 =",var_cheeze_value11.get())
vars()[str("var_cheeze_sum"+str(serie))] += vars()[str("var_cheeze_value"+str(id-val+o)+str(serie))].get()
kanvas.pack()
fenetre.mainloop()
vars()[str("nombre_de_formes"+str(serie))] =int(float(input("combien?")))
val = vars()[str("nombre_de_formes"+str(serie))]
for o in range(1,val+1):
id+=1
vars()[str("var_cheeze_value"+str(id)+str(serie))] = Entry(kanvas, width=10)
o+=1
vars()[str("var_cheeze_value"+str(id)+str(serie))].pack
kanvas.pack()
fenetre.mainloop()
Traceback (most recent call last):
File "C:\Users\Utilisateur\AppData\Local\Programs\Python\Python37-32\lib\tkinter\__init__.py", line 1705, in __call__
return self.func(*args)
File "C:/Users/Utilisateur/.PyCharmEdu2019.1/config/scratches/scratch_1.py", line 38, in cheezegrater
vars()[str("var_cheeze_sum"+str(serie))] += vars()[str("var_cheeze_value"+str(id-val+o)+str(serie))].get()
KeyError: 'var_cheeze_value11'
You're inside a function and therefore outside of the same namespace where vars() doesn't contain those variables. Have a look at this example:
x = 1
print('Outside', x, 'x' in vars())
def f():
global x
print('Inside', x, 'x' in vars())
f()
It prints:
Outside 1 True
Inside 1 False
As you can see, even though we have global x and can print its value, it's not a key in vars() inside the function.
Also: Why are global variables evil?
Why did you choose to use vars() in the first place? Maybe you could just use a separate dict object instead? There's a lot of information in this question Python: Using vars() to assign a string to a variable
An improved version of the example from above might look like this:
data = {}
key = 'x'
data[key] = 1
print('Outside', data['x'], 'x' in data)
def f(data):
print('Inside', data['x'], 'x' in data)
f(data)
Of course, instead of 'x', you can use your own keys like str("var_cheeze_sum"+str(serie)).
Related
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
AMOUNT = 1
x = 175
y = 175
def main():
screen = pygame.display.set_mode((600,600))
screen.fill( (251,251,251) )
BoxAmountCalc(humaninput)
DrawBoxCalc()
pygame.display.flip()
while True:
for event in pygame.event.get():
if event.type == QUIT:
return
def BoxAmountCalc(x):
x = (2**humaninput) * (2**humaninput)
size = 600/x
return size
def DrawBoxCalc():
while True:
pygame.draw.rect(screen,(0,0,0), (x,y,size,size))
AMOUNT += 1
x = x + size
x = y + size
pygame.display.flip()
if AMOUNT > humaninput:
break
I've left out a few parts of the code, some of the variable definitions, but when I try to run this code it gives me an error saying that "screen" is not defined.
Is this because I need it to be defined as a parameter for the function and then pass it into the function, or am I missing something completely here?
Thank you for looking, I'm sorry for a very beginner question.
Is this because I need it to be defined as a parameter for the
function and then pass it into the function.
Yes. Once a function finishes executing, the variables created therein are destroyed. Here is an example:
def go():
x = 10
go()
print(x)
--output:--
Traceback (most recent call last):
File "1.py", line 5, in <module>
print(x)
NameError: name 'x' is not defined
Same thing here:
def go():
x = 10
def stay():
print(x)
go()
stay()
--output:--
File "1.py", line 9, in <module>
stay()
File "1.py", line 6, in stay
print(x)
NameError: name 'x' is not defined
But:
x = 10
def go():
print(x)
go()
--output:--
10
And better:
def go(z):
print(z)
x = 10
go(x)
--output:--
10
Try to keep your functions self contained, which means they should accept some input and produce some output without using variables outside the function.
In your code, you can do:
DrawBoxCalc(screen) and def DrawBoxCalc(screen):
but you also have an issue with humaninput. I would try to define DrawBoxCalc as DrawBoxCalc(humaninput, screen), and call it with both args. That means you will have to define main as main(humaninput).
Also, function names should start with a lower case letter, and python uses what is called snake_case for lower case names, so draw_box_calc, and class names should start with a capital letter and they can use camel case: class MyBox.
I am trying to calculate the number of elements in a chemical equation. The debugger that I have created somehow doesn't have access to the globals within my program. Specifically, I am trying to access carrots but left is not being added to the stack. Any ideas?
Debug.py
class Debugger(object):
def __init__(self,objs):
assert type(objs)==list, 'Not a list of strings'
self.objs = objs
def __repr__(self):
return '<class Debugger>'
def show(self):
for o in self.objs:
print o,globals()[o] #EDIT
Chemical_Balancer.py
from Debug import Debugger
def directions():
print 'Welcome to the chem Balancer.'
print 'Use the following example to guide your work:'
global left #LEFT IS GLOBAL
left = 'B^6 + C^2 + B^3 + C^3 + H^9 + O^4 + Na^1'
print left
print "#Please note to use a 'hat' when entering all elements"
print '#use only one letter elements for now'
# left = raw_input('enter formula:') #enter formula to count
directions()
chem_stats = {}
chem_names = []
chem_names = []
chem_indy = []
for c in range(len(left)):
if left[c].isalpha() and left[c].isupper():
chars = ''
if left[c+1].islower():
chars += left[c]+left[c+1]
else:
chars += left[c]
#print chars
chem_indy.append(c)
chem_names.append(chars)
carrots = [x for x in range(len(left)) if left[x]=='^']
debug = Debugger(['carrots','chem_names','chem_indy','chem_stats']) # WITHOUT LEFT
debug.show()
Error message:
Traceback (most recent call last):
File "C:\Python27\#Files\repair\Chemical_Balancer.py", line 38, in <module>
debug.show()
File "C:\Python27\lib\site-packages\Debug.py", line 12, in show
print o,globals()[o]
File "<string>", line 1, in <module>
KeyError: 'carrots'
About the specific error on the left variable:
when you say a variable is global, python knows it has to look it up in the global namespace when its name is used. But in the code left hasn't been assigned in such namespace.
As you can see, left is commented out
#left = raw_input('enter formula:') #enter formula to count
Uncomment it by removing the # at the beginning of the line, so the line inside the directions function
global left
can find it and the instructions that follow can work.
About the implementation:
one solution to allow the debugger to know where to look for the variables, i.e. in which module, can be to provide the name of the module to it when it is created. Then the debugger object can reach the global variables of the module that created it via sys.modules[module_name].__dict__
debugger.py
import sys
class Debugger(object):
def __init__(self, module_name, objs):
assert type(objs)==list,'Not a list of strings'
self.objs = objs
self.module_name = module_name
def __repr__(self):
return '<class Debugger>'
def show(self):
for o in self.objs:
print o, sys.modules[self.module_name].__dict__[o]
chemical_balancer.py
import debugger as deb
a = 1
b = 2
d = deb.Debugger(__name__, ['a', 'b'])
print(d.objs)
d.show()
a = 10
b = 20
d.show()
which produces
['a', 'b']
a 1
b 2
a 10
b 20
As you can see, the debugger prints the current value of the variables each time its show method is called
I have found this SO Q&A informative and helpful.
I wrote some code that was meant to try to approach a target string by selecting randomly from a list of chars, but I have some problem that I do not quite understand.
import random
class MonkiesGo:
__chars = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
def __init__(self, targetString):
__targetString = targetString.lower()
__targetList = list(targetString)
__attemptList = []
__unmatchedIndexes = [ x for x in range(0, (len(targetString)-1)) ]
def attemptToSolve(self):
if len(__unmatchedIndexes) == 0:
__attemptString = ''.join(__attemptList)
return __attemptString, __targetString
else:
for index in __unmatchedIndexes:
__attemptList[index] = randomChar()
def updateSolutionProgress(self):
for indexCheck in __unmatchedIndexes:
if __targetList[index] == __attemptList[index]:
__indexToClear = __unmatchedIndexes.index(index)
del __unmatchedIndexes[indextToClear]
def __randomChar(self):
return __chars[ random.randint(0,26) ]
when I import it into a python shell in my terminal, make an object as follows:
from monkies import MonkiesGo
mk = MonkiesGo("hello")
mk.attemptToSolve()
I get the error:
Traceback (most recent call last):
File "", line 1, in
File "path/to/the/file/monkies.py", line 15, in attemptToSolve
if len(__unmatched 0: NameError: name '_MonkiesGo__unmatched' is not defined
What is causing this, and why is there an underscore before MonkiesGo?
THanks in advance.
Updated to:
import random
class MonkiesGo:
def __init__(self, targetString):
self.targetString = targetString.lower()
self.targetList = list(targetString)
self.attemptList = []
self.unmatchedIndexes = [ x for x in range(0, (len(targetString)-1)) ]
self.chars = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
def attemptToSolve(self):
if len(self.unmatchedIndexes) == 0:
self.attemptString = ''.join(self.attemptList)
return self.attemptString, self.targetString
else:
for index in self.unmatchedIndexes:
self.attemptList[index] = randomChar()
def updateSolutionProgress(self):
for indexCheck in self.unmatchedIndexes:
if self.targetList[index] == self.attemptList[index]:
indexToClear = self.unmatchedIndexes.index(index)
del self.unmatchedIndexes[indextToClear]
def randomChar(self):
return self.chars[ random.randint(0,26) ]
Now I get a name error regarding randomChar..?
You are not creating any instance variables. In the function __init__, variables such as __targetString are local and are defined only within the function. When you call the function attemptToSolve, the variable __unmatchedIndices is also local and therefore undefined. The double underscore does not automatically make an instance variable; perhaps that's your confusion.
Instead of __targetString = whatever, you should use self.__targetString = whatever, or better yet drop the underscores and use just self.targetString. That creates a member variable. Access it in member functions using the same self.targetString syntax. Check out the tutorial that comes with Python.
comodin.py
def name():
x = "car"
comodin_1.py
import comodin
print comodin.x
Error:
Traceback (most recent call last):
File "./comodin_2.py", line 4, in <module>
print comodin.x
AttributeError: 'module' object has no attribute 'x'
Is this possible?
In the code you wrote, "x" doesn't exist in "comodin". "x" belongs to the function name() and comodin can't see it.
If you want to access a variable like this, you have to define it at the module scope (not the function scope).
In comodin.py:
x = "car"
def name():
return x
In comodin_1.py:
import comodin
print comodin.name()
print comodin.x
The last 2 lines will print the same thing. The first will execute the name() function and print it's return value, the second just prints the value of x because it's a module variable.
There's a catch: you have to use the 'global' statement if you want to edit the value "x" from a function (add this at the end of comodin.py):
def modify_x_wrong():
x = "nope"
def modify_x():
global x
x = "apple"
And in comodin_1.py:
print comodin.name() # prints "car"
comodin.modify_x_wrong()
print comodin.name() # prints "car", once again
comodin.modify_x()
print comodin.name() # prints "apple"