In my math class, we have been told to write a program in python. In one of the parts, I want to test the convergence of a series. While writing the program, I realized I was fundamentally misunderstanding something about how python handles global variables. Take this code:
def main():
global n
n = 1
def check():
a = 10
if n > a: print(n)
else: n += 1
check()
main()
This code fails because it says that n has not been defined yet. However, I can't define n inside of the check() function since that would just reset n to one upon every iteration! Is there some workaround for this problem?
As already stated in the comments, n isn't in the global scope yet, since it's inside the nested function check. You will need to add global n to the check's scope, to access the global n value from the nested function:
def main():
global n
n = 1
def check():
global n
a = 10
if n > a: print(n)
else: n += 1
check()
main()
#PedrovonHertwig also pointed out you don't need global n in main (which is the case in your current context, ignore this if you want to use n elsewhere in the top-level scope) and thatn is perfectly fine staying in main's local scope. You can then replace the global keyword inside check to nonlocal n, telling python to use the n that is not in the local scope nor the global scope, but main's scope.
Related
I tried to count the total number of calling times via a recursive function that generates n for-loops, but the variable seems to never change its value.
a=0
def recursivelooping(times,volumes):
if times==0:
a+=1
else:
for i in range(volumes):
return recursivelooping(times-1,i)
The result should look more like the variable a below, but instead I always got a zero.
def multiforloop(volumes):
a=0
for i in range(volumes):
for j in range(i):
for k in range(j):
a+=1
print(a)
If I understand correctly having a closure maybe the shortest solution:
def recursivelooping():
a = 0
def f(times, volumes):
nonlocal a
if volumes == 0:
return a
# Inner loop
for t in range(times):
for j in range(volumes):
a+=1
# Outer loop
return f(times, volumes-1)
return f
def multiforloop(times, volumes):
a=0
for i in range(volumes+1):
for t in range(times):
for j in range(i):
a+=1
return a
print(recursivelooping()(1, 10))
print(multiforloop(1, 10))
This will print 55 for both (as in n*(n+1)/2). A closure simply is a function (here f) accompanied by an environment (some context that maps names to variables, here a alone). That mean that a is accessible from within f as if it was a local variable, but it's not exactly a local variable it's a free variable
When you write a += 1 the normal behavior is to look for a local variable named a, instead here a is a free variable, that's why we need to add the nonlocal keyword to specify this.
I am reading Hackers and Painters and am confused by a problem mentioned by the author to illustrate the power of different programming languages.
The problem is:
We want to write a function that generates accumulators—a function that takes a number n, and returns a function that takes another number i and returns n incremented by i. (That’s incremented by, not plus. An accumulator has to accumulate.)
The author mentions several solutions with different programming languages. For example, Common Lisp:
(defun foo (n)
(lambda (i) (incf n i)))
and JavaScript:
function foo(n) { return function (i) { return n += i } }
However, when it comes to Python, the following codes do not work:
def foo(n):
s = n
def bar(i):
s += i
return s
return bar
f = foo(0)
f(1) # UnboundLocalError: local variable 's' referenced before assignment
A simple modification will make it work:
def foo(n):
s = [n]
def bar(i):
s[0] += i
return s[0]
return bar
I am new to Python. Why doesn the first solution not work while the second one does? The author mentions lexical variables but I still don't get it.
s += i is just sugar for s = s + i.*
This means you assign a new value to the variable s (instead of mutating it in place). When you assign to a variable, Python assumes it is local to the function. However, before assigning it needs to evaluate s + i, but s is local and still unassigned -> Error.
In the second case s[0] += i you never assign to s directly, but only ever access an item from s. So Python can clearly see that it is not a local variable and goes looking for it in the outer scope.
Finally, a nicer alternative (in Python 3) is to explicitly tell it that s is not a local variable:
def foo(n):
s = n
def bar(i):
nonlocal s
s += i
return s
return bar
(There is actually no need for s - you could simply use n instead inside bar.)
*The situation is slightly more complex, but the important issue is that computation and assignment are performed in two separate steps.
An infinite generator is one implementation. You can call __next__ on a generator instance to extract successive results iteratively.
def incrementer(n, i):
while True:
n += i
yield n
g = incrementer(2, 5)
print(g.__next__()) # 7
print(g.__next__()) # 12
print(g.__next__()) # 17
If you need a flexible incrementer, one possibility is an object-oriented approach:
class Inc(object):
def __init__(self, n=0):
self.n = n
def incrementer(self, i):
self.n += i
return self.n
g = Inc(2)
g.incrementer(5) # 7
g.incrementer(3) # 10
g.incrementer(7) # 17
In Python if we use a variable and pass it to a function then it will be Call by Value whatever changes you make to the variable it will not be reflected to the original variable.
But when you use a list instead of a variable then the changes that you make to the list in the functions are reflected in the original List outside the function so this is called call by reference.
And this is the reason for the second option does work and the first option doesn't.
n=0
def add(n):
print n
n+=1
print n
return n
whatever I do to "n" it is returned as 0. If I start n=0 and use add(n) I get n=0 back again. The "print n" after "n+=1" is "n=1". "return n" somehow transforms "n" back to "0" or does it have to do with a global "n=0" that I cannot change no matter what? I don't understand what is going on. How do I create a variable that can be changed in functions (from one function to another...).
n is a local variable in add(), and as such it is not the same variable as the global n. The two are (quite deliberately) not connected.
If you want to change the global, you have two options:
reassign the return value from add() back to the global n:
n = add(n)
alter the global directly by marking the name n in add as a global. Your add() function doesn't need to use an argument in that case:
def add():
global n
n += 1
I'm trying out the following code with global, and nonlocal scope. following snippet working wihtout any issues.
def countdown(start):
n = start
def display():
print('--> %d' % n)
def decrement():
nonlocal n ##using python3
n -= 1
while n > 0:
display()
decrement()
countdown(10)
countdown(10)
but why can't i use the global n ? instead of nonlocal n. that gives me
UnboundLocalError: local variable 'n' referenced before assignment
this is the snippet
def countdown(start):
global n ##defined it global
n = start
def display():
print('--> %d' % n)
def decrement():
##no nonlocal varibale here
n -= 1
while n > 0:
display()
decrement()
countdown(10)
The global declaration doesn't automatically apply to nested functions. You need another declaration:
def decrement():
global n
n -= 1
so n in decrement also refers to the global variable.
You need to mark a variable as global in every function where you use it (or rather, every one where you assign to it). You marked n as global in countdown, but decrement still thinks it is local. If you want decrement to also use the global n, you need to put another global n inside decrement.
Okay, I am currently doing a project to make a blackjack game in python and I'm having some trouble. One of my issues is I dont know when to define a variable as global, specifically in functions with if statements. If I have a global variable outside the if statement, do I have to claim that the variable is global within the if statement as well? For example:
x = 5
def add():
global x <--- ?
x += 1
if x == 7:
global x <--- ?
x = 5
I'm pretty sure I need the "global x" at the 1st question mark, but what about at the second question mark? Would I still need to put a "global x" within my if statement if I wanted my if statement to update a global variable? Or does the global x at the beginning of the function make the x's inside the if statement global? Also, if I wanted to return x here, where should I do it?
Only one global statement is enough.
From docs:
The global statement is a declaration which holds for the entire
current code block.
x = 5
def add():
global x
x += 1
if x == 7:
x = 5
Also, if I wanted to return x here, where should I do it?
If you're using global in your function then the return x must come after the global x statement, if you didn't use any global statement and also didn't define any local variable x then you can return x anywhere in the function.
If you've defined a local variable x then return x must come after the definition.