Why is day_num not defined? - python

for day_num in range(1,8):
sales=float(input("Enter the sales for Day {}".format(day_num)))
bakery_temp_info.append(sales)
bakery_sale.append(list(bakery_temp_info))
del bakery_temp_info[:]
This is a section of an exemplar code that my teacher gave me. Can anyone explain how the variable "day_num" is used for this loop when it has not been previously defined. I have tried printing it out in the loop and it increases by 1 every time the loop runs. Any help appreciated, thanks.

Python variables don't have to be declared; the first time you assign to one, it's created.
And almost anything that assigns a value to a name counts as an assignment, not just actual = statements. That includes:
name = …
while (name := …) > 0:
for name in …:
import name
with … as name:
def func(name):
… and so on.
Each of these assigns a value to name in the current scope (except for the last one, which only assigns a value in the scope of the function body). It doesn't matter whether name was a variable before that statement or not; it is one now.
If it isn't clear where any assignment is happening, your for loop is equivalent to this while loop:
_range18iter = iter(range(1, 8))
try:
while True:
day_num = next(_range18iter)
# body of the for loop
except StopIteration:
pass
def _range18iter
Now it's obvious that, as long as the iterable is not empty, day_num is going to be assigned to.

You are declaring day_num in the loop. Basically the loop is telling the interpreter that you want to iterate over something and want to give each iteration a name, in this case day_num.
The iterable you are providing in this case is a range from 1-8, but you could just as easily pass it a list. In that case you would be calling each element of the list day_num, as an example.
Every time you run through the loop, it updates the value stored in day_num.

Related

Rename a variable each time the code is run (python)?

I have a bit of python code that's set to run on a schedule. (I think my problem is the same as if it were within a loop.)
Let's say that in its most basic form, the code snippet looks something like this:
A = 1
B = 2
renameMe = A + B
Let's say the scheduler runs the same snippet of code every 5 minutes. The values of variables A & B are different each time the code is run, but the operation renameMe = A + B is always the same.
The values for A & B are grabbed out of a dataframe that's updated every 5 minutes, so I don't know what they are in advance, but if I need to do something with them beforehand instead of assigning them to A & B right away, I can.
I recently found out that for other things to work, I need to be able to rename the variable renameMe every time that snippet of code runs. In other words, I want the variable's name to be renameMe1 the first time the code snippet runs, then renameMe2 when it runs 5 minutes later, and so on.
It doesn't really matter in which way the variable's name changes (ints, strs, whatever) as long as I'm able to find out what the new variable name is, and use it elsewhere.
Do NOT use a variable variable name, you will have problems, use a container:
a list:
# first time
container = []
# each loop/run
container.append(A+B)
## last value
container[-1]
a dictionary:
# first time
container = {}
# each loop/run
container['new_id'] = A+B
# access arbitrary value
container['my_previous_id']
If you need persistence, use a flat file or a database.
I think it is suitable to use a class so that setattr can be used:
class newVal:
def __init__(self):
self.n = 1
def addVal(self, a, b):
setattr(self, f”val{self.n}”, a+b)
self.n += 1
Values = newVal()
Values.addVal(a, b)
Values.val1 would now be assigned
I aggree with Mozway when saying variables names are likely to cause problems, but this is also something you could strictly manage.
globals() stores all variables names and values in the form of a collection of 2-tuples, like this one :
dict_items([('__name__', '__main__'), ..., ('thisName', 'renaMe1'), ('renaMe18', 10)])
So you should register your new variable name but not forget to delete the previous one in order to avoid overloading.
If you follow a natural law of equal births and deaths, you will avoid overpopulation.
I propose you this bunch of code (with comments inside) :
basename = 'renaMe'
def varUpdate():
# Get previous variable name
thisName = [i for i, j in globals().items() if i[:len(basename)] == basename][0]
# Define the new variable name
newName = basename + '%d'%sum([int(thisName[len(basename):]), 1])
# Register the new variable name
globals()[newName] = globals()[thisName]
# Delete previous variable name from global
del globals()[thisName]
def process(i):
# Isolate from process content for readibility
varUpdate()
# PROCESS BELOW
# ....
newVar = [i for i, j in globals().items() if i[:len(basename)] == basename][0]
print(newVar, " : ", globals()[newVar])
# With this for` loop we simulate 4 entries in process
for i in range(4):
### we enter in the process
process(i)
Test in the shell
First restart your shell and let's suppose we have at the beginning renaMe12 = 12 :
>>> renaMe12 = 12
>>> Proposed Script ...
Result
Variable increments it's proper name at each iteration.
renaMe13 : 12
renaMe14 : 12
renaMe15 : 12
renaMe16 : 12
If you check in the shell now, you could see at the end of iteration, renaMe12 to renaMe15 no longer exist.
Only the variable renaMe16 exists with value 12.
>>> renaMe16
12
>>>> renaMe15
Retraçage (dernier appel le plus récent) :
Shell Python, prompt 4, line 1
builtins.NameError: name 'renaMe15' is not defined
Conclusion
This discussion is just for the sake of experimentation, but if I were you I would do my possible to avoid such code complexification unless it's necessary.
I agree Mozway when thinking you should avoid pain headaches...

Use a dictionary to refer to a global variable inside a function (Python)

I'm using a dictionary to refer to a global variable in Python inside a function. I want to use the function to update the global variable. (In reality my global variables are more complicated than this.)
global_variable_A=5
global_variable_B=1
dictionary={'A':global_variable_A,'B':global_variable_B}
def f(x,glob):
global global_variable_A
dictionary[glob]+=x
f(2,'A')
print(global_variable_A)
This returns 5 rather than 7. I understand why, is there anyway to let Python know that I mean the variable dictionary[glob] to refer to the global rather than the local variable, while still referring to the variable through a dictionary?
Thanks for your time, and apologies if I've missed something completely obvious.
When you assign a value to a name name = 5, you're creating a reference to 5 that you can use the identifier name to access. Normally, if you then have some code with a narrower scope, you can either use that reference
def f():
print(name)
or create a local reference using the same identifier, potentially to an unrelated value
def g():
name = 100
print(name)
The global keyword allows you to instead manipulate the identifier as if you weren;t in the more narrow scope, allowing you to reassign the global name to a different reference:
def h():
global name
name = 100
h()
print(name) # 100
However, when you use a reference to create another reference, there isn't any relation ship between those two references. So
name = 5
l = [name]
leaves us with two references to the value 5: one from the identifier name, and one from the first position of l. Crucially, those two references are not related; we can change one without changing the other.
name = 6
print(l) # [5]
One way to accomplish what you want is to use a boxed type. That means that you create an object that points to another object. All of your references can then point to the first object (the box) and you can freely change what it points to (the thing "inside" the box), by only updating a single reference:
class Box:
def __init__(self, value):
self.value = value
box = Box(5)
l = [box]
box.value = 10
print(l[0].value) # 10
If you think you need to do this, there's a design flaw in your code. I really wouldn't recommend doing this.
global_a = 5
def add_to_global(key, value):
globals()[key] += value
add_to_global("global_a", 2)
print(global_a)
Output:
7

local variable 'mes' referenced before assignment in python?

I'm working on this function that converts a Julian day to a gregorian date, the output should look like ex. 22.0,12.0,2000 thats why in print I have those three variables, but when I run it, it gives me the error:
local variable 'mes' referenced before assignment.
Also, "A" appears to be highlighted in yellow with the warning "local variable A might be referenced before assignment". I don't know how to fix it.
I've tried changing the indentation, but I don't see what's wrong.
Below is my function:
def JD2fechaGregoriana(fecha):
fecha1=int(fecha+0.5)
fecha2=float(fecha1-fecha)
Z =fecha1
F = fecha2
if (Z<2299161):
A=Z
elif (Z>= 2291161):
alpha=int((Z-1867216.25)/36524.25)
A= Z + 1 + alpha -int(alpha/4)
B= A +1524
C= int((B-122.1)/365.25)
D= int(365.25*C)
E= int((B-D)/30.6001)
dia1= B-D - int(30.6001*E)+F
if (E>14):
mes=E-1
elif (E==14 or E==15):
mes=E-13
if (mes>2):
anio=C-4716
elif(mes==1 or mes==2):
anio=C-4715
print(dia1,mes,anio)
Thank you in advance.
You are using variable mes in an if...else and then printing its value.
Even though variable mes exists, you are assigning its value inside if...else block, naturally this will create variable inside the if block, and therefore when you try to print mes it gives the error:
You should first declare variable as 0 before if statement, and then further use this in if block.
eg:
.....
mes=0
if (E>14):
mes=E-1
......
And same goes with variable A.
It would work!
Friend, review the topic of the scope of the variables, declare the variables before comparing them in IF -ELSE. Initialize them with some value. For example
month = 0
A = 0

Tkinter Commands: Function arguments in lambdas?

My program has a lambda as a command for a tkinter object inside a loop. I want the lambda to pass an argument for a function where the function will make some changes according to the argument.
for cat in self.t_m_scope:
print(cat)
self.t_m_scopeObj[cat] = [tk.Checkbutton(self, text = cat, command = lambda: self.t_m_checkUpdate(cat)), []]
if self.t_m_scopeVars[cat][0]: self.t_m_scopeObj[cat][0].toggle()
self.t_m_scopeObj[cat][0].grid(row = 5, column = colNum, padx = (10,10), pady = (2,5), sticky = tk.W)
Whenever I try to run this, it always passes in the last iteration of cat because of some weird nature in tkinter commands. Instead, I want it so that the argument matches exactly whatever iteration it was at when the lambda was defined. I don't want the argument to change just because I clicked the check button after its iteration.
Here is the function that the lambda calls.
def t_m_checkUpdate(self, cat):
self.t_m_scopeVars[cat][0] = not self.t_m_scopeVars[cat][0]
for subcat in range(len(self.t_m_scopeVars[cat][1])):
if self.t_m_scopeVars[cat][0] != self.t_m_scopeVars[cat][1][subcat]:
self.t_m_scopeVars[cat][1][subcat] = not self.t_m_scopeVars[cat][1][subcat]
self.t_m_scopeObj[cat][1][subcat].toggle()
As some context to what this program is, I am trying to have a bunch of checkbuttons toggle on and off in response to a click on a main checkbutton. The only way I know which checkbuttons to toggle (since I have many) is to pass an argument to a function using lambda.
I do understand that I could just find the value of the current iteration using if/elif/else and pass a string instead of a variable but I seriously don't want to do that because it's just not scalable.
If it helps, the text for my checkbutton and the argument I want to pass is one and the same.
Is there any workaround so I can preserve the current iteration without Tkinter going all wonky?
You need to bind the data in cat to the function when it's created. For an explanation, see this question. There are a few ways to do this, including partials and closures.
This is how you would apply a partial to your example:
from functools import partial
for cat in ...:
check_update_cat = partial(self.t_m_checkUpdate, cat)
self.t_m_scopeObj[cat] = [tk.Checkbutton(self, text=cat, command=check_update_cat), []]
Explanation
In your code, the lambda refers to an iterator variable (cat) declared in the outer scope of the loop. One thing to note is that the scope of a loop "leaks" the iterator variable, i.e. you can still access it after the loop. The lambda functions refer to the iterator variable by reference, so they access the most recent value of the iterator.
A runnable example:
a = []
for i in range(5):
a.append(lambda: i)
a[0]() # returns 4
a[1]() # also returns 4
del i # if we delete the variable leaked by the for loop
a[0]() # raises NameError, i is not defined because the lambdas refer to i by reference
Instead, we want to bind the value of the iterator to the functions at each step. We need to make a copy of the iterator's value here.
import functools
a = []
for i in range(5):
a.append(functools.partial(lambda x: x, i)) # i is passed by value here
a[0]() # returns 0
a[1]() # returns 1
del i
a[0]() # still returns 0

Why does the scope work like this?

This question is a result of a student of mine asking a question about the following code, and I'm honestly completely stumped. Any help would be appreciated.
When I run this code:
#test 2
a = 1
def func2(x):
x = x + a
return(x)
print(func2(3))
it works perfectly fine. It is able to take the globally-scoped variable a and use its value to perform the calculation and return the value 4.
However, if I change it to this:
# test 3
a = 1
def func3(x):
a = x + a
return(x)
print(func3(3))
I then get an error:
local variable 'a' referenced before assignment
Why do I get this error only when I want to update the value of a within the function to a new value based on its original value? What am I not understanding? I feel like this second piece of code should work fine.
Thanks in advance for any help and insight.
a = 1
def func3(x):
global a
a = x + a
return(x)
print(func3(3))
Now it should work.
When you put the statement a=x+a inside the function, it creates a new local variable a and tries to reference its value(which clearly hasnt been defined before). Thus you have to use global a before altering the value of a global variable so that it knows which value to refer to.
EDIT:
The execution of a function introduces a new symbol table used for the
local variables of the function. More precisely, all variable
assignments in a function store the value in the local symbol table;
whereas variable references first look in the local symbol table, then
in the local symbol tables of enclosing functions, then in the global
symbol table, and finally in the table of built-in names. Thus, global
variables cannot be directly assigned a value within a function
(unless named in a global statement), although they may be referenced.
def func3(x):
a = x + a
return(x)
On the right hand right side of a = x + a (So, x + a), 'x' is passed as variable, where 'a' is not passed as a variable thus an error.
Without using globals:
a = 1
def func3(x, a=2):
a = x + a
return(x)
func3(3)
Returns: 5

Categories

Resources