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.

Asked By: ainu

||

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.

Answered By: Volatility

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.

Answered By: User

@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.

Answered By: Benjamin Hodgson
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.