UnboundLocalError: int vs list
Question:
I know that this error message has been discussed already a lot but I haven’t found an explanation of the following:
def random2(seed):
x = seed
def update():
x = ( x * 16807 ) % 2147483647
return x
return update
r = random2(17283945)
print(r())
This doesn’t work because the scope of the variable x seems to get lost by returning the function [UnboundLocalError]. Ok. But now I’ve found out that there is absolutely no problem with
def random(seed):
x = [seed]
def update():
x.append(( x.pop() * 16807 ) % 2147483647 )
return x[0]
return update
r = random(17283945)
print(r()) #580971270
print(r()) #1923475628
print(r()) #1783541505
I’m a little confused why, in this case, the scope of x remains valid. It seems that is has something to do with (im)mutability, but this still doesn’t make a lot of sense to me.
Thanks a lot.
Answers:
When you put an assignment operator (=
) after a variable name, that variable is automatically assumed to be local. Because you are trying to seemingly reference it before it’s assignment, you get the error.
In the second example, you never assigned a variable x
to anything, you merely mutated it in-place.
If you are using Python 3 you can use nonlocal x
def random2(seed):
x = seed
def update():
nonlocal x
x = ( x * 16807 ) % 2147483647
return x
return update
r = random2(17283945)
print(r())
In python 2 I always do what you did with the list.
@volatility has explained why it happens, but for reference here’s how you might write a similar function that avoids nested scopes using generators.
def random(seed):
x = seed
while True:
x = ( x * 16807 ) % 2147483647
yield x
This will give you a sequence of pseudorandom numbers when iterated over like for rand_num in random(100)
. You can also get new random numbers on demand without using a for loop: rand_gen = random(100); rand_num = next(rand_gen)
.
You can see that random number generators are a natural use for generators: the function is shorter, clearer, can be used in a natural way (for...in
and next
) and less prone to the errors that can arise due to nonlocal
. See this question for a better explanation of generators and yield
.
I know that this error message has been discussed already a lot but I haven’t found an explanation of the following:
def random2(seed):
x = seed
def update():
x = ( x * 16807 ) % 2147483647
return x
return update
r = random2(17283945)
print(r())
This doesn’t work because the scope of the variable x seems to get lost by returning the function [UnboundLocalError]. Ok. But now I’ve found out that there is absolutely no problem with
def random(seed):
x = [seed]
def update():
x.append(( x.pop() * 16807 ) % 2147483647 )
return x[0]
return update
r = random(17283945)
print(r()) #580971270
print(r()) #1923475628
print(r()) #1783541505
I’m a little confused why, in this case, the scope of x remains valid. It seems that is has something to do with (im)mutability, but this still doesn’t make a lot of sense to me.
Thanks a lot.
When you put an assignment operator (=
) after a variable name, that variable is automatically assumed to be local. Because you are trying to seemingly reference it before it’s assignment, you get the error.
In the second example, you never assigned a variable x
to anything, you merely mutated it in-place.
If you are using Python 3 you can use nonlocal x
def random2(seed):
x = seed
def update():
nonlocal x
x = ( x * 16807 ) % 2147483647
return x
return update
r = random2(17283945)
print(r())
In python 2 I always do what you did with the list.
@volatility has explained why it happens, but for reference here’s how you might write a similar function that avoids nested scopes using generators.
def random(seed):
x = seed
while True:
x = ( x * 16807 ) % 2147483647
yield x
This will give you a sequence of pseudorandom numbers when iterated over like for rand_num in random(100)
. You can also get new random numbers on demand without using a for loop: rand_gen = random(100); rand_num = next(rand_gen)
.
You can see that random number generators are a natural use for generators: the function is shorter, clearer, can be used in a natural way (for...in
and next
) and less prone to the errors that can arise due to nonlocal
. See this question for a better explanation of generators and yield
.