3

I get an error when I run this python script.

def thousandthPrime():
    count=0
    candidate=5 #candidates for prime no. these are all odd no.s Since starts at 5 therefore we find 998th prime no. as 2 and 3 are already prime no.s
    while(True):
        #print 'Checking =',candidate
        for i in range(2,candidate/2): #if any number from 2 to candidate/2 can divide candidate with remainder = 0 then candidate is not a prime no.
            if(candidate%i==0):
                break
        if i==(candidate/2)-1: # If none divide it perfectly, i will reach candidate/2-1 eventually. So, this is a prime number.
            count+=1
            print 'No. of prime no.s found excluding 2 and 3 =',count, '--->',candidate
        if(count==998):
            print 'The thousandth prime is',candidate
            break
        candidate+=2 # to go to the next odd number.

I get this error:

File "/home/.../xxx.py", line 19, in thousandthPrime
    if i==(candidate/2)-1: # If none divide it perfectly, i will reach candidate/2-1 eventually. So, this is a prime number.
UnboundLocalError: local variable 'i' referenced before assignment

But if I replace, candidate/2 with just candidate, I get no errors, although it increases some unnecessary calculations.

Kevin
  • 74,910
  • 12
  • 133
  • 166
aste123
  • 1,223
  • 4
  • 20
  • 40
  • 3
    Already explained in docs: [Why am I getting an UnboundLocalError when the variable has a value?](http://docs.python.org/2/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value) – Ashwini Chaudhary Nov 13 '13 at 18:29
  • 1
    I'm not so sure. The docs make it sounds like this only happens if the variable is assigned to in local scope. Here, `i` is never assigned to, just used for comparison. – ely Nov 13 '13 at 18:32
  • Yes, @CDspace's answer seems to be on the right track. This link to the UnboundLocalError doc is not relevant in this instance. – ely Nov 13 '13 at 18:35
  • @ely `i` **is** assigned to, that's the thing. `for i in ...` is assignment to `i`. – Karl Knechtel Feb 07 '23 at 03:49

3 Answers3

6

You're declaring candidate as an integer. Thus candidate/2 is also an integer, specifically 2. Then your range(2, candidate/2) is range(2, 2) which is nothing, so i is never initialized. You'll need to set candidate=5.0 to make it a float and all should be well.

EDIT As pointed out in the comments, simply re-defining candidate will give you a type error, but it should be enough to get you on track. Note, range(x, y) expects integers, you you may have to convert to an integer again after the division or limiting calculation using int(). Also, you may want to look into why the math.sqrt function was mentioned related to primality testing

CDspace
  • 2,639
  • 18
  • 30
  • 36
0

The problem is simple if you step through it:

>>> candidate=5
>>> candidate/2
2
>>> range(2, 2)
[]
>>> for i in []: print(i)
>>> i
NameError: name 'i' is not defined

Your loop is empty, so you never step into it, so i never gets defined.

As pointed out in comments elsewhere, you really only need to check up to int(sqrt(candidate)), not candidate/2. But you'll still have the same problem if you do that: int(sqrt(5)) is still 2.


This means this test:

if i==(candidate/2)-1:

… is trying to access a variable that hasn't been defined.

But if you step back and think about what you're really testing, in pseudocode, it's this:

if I finished the loop without break:

And Python has a built-in way to say that: the else clause on loops.

In fact, the tutorial section break and continue Statements, and else Clauses on Loops which introduces this feature has an example very similar to yours:

>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...             print n, 'equals', x, '*', n/x
...             break
...     else:
...         # loop fell through without finding a factor
...         print n, 'is a prime number'

The else clause will be executed if you finish looping without a break—which includes the case where the loop iterator was empty.


So, in your code:

while(True):
    #print 'Checking =',candidate
    for i in range(2,candidate/2): #if any number from 2 to candidate/2 can divide candidate with remainder = 0 then candidate is not a prime no.
        if(candidate%i==0):
            break
    else: # If none divide it perfectly, i will reach candidate/2-1 eventually. So, this is a prime number.
        count+=1
        print 'No. of prime no.s found excluding 2 and 3 =',count, '--->',candidate

If you want more detail on exactly how else works than the tutorial gives, see the reference docs on while and for.

abarnert
  • 354,177
  • 51
  • 601
  • 671
-1

Scope of a variable

That's not a problem in this code, which even if it's unpretty to use the variable i outside its intended scope (inside the for construct), i should remain defined after finishing the for loop, provided...

Checking edge cases

... the for loop should run at least once to declare the name i for some variable.

As this doesn't happen, you are getting an UnboundLocalError.

Quick fix

You can fix this by declaring the i variable just before the for loop, in case it doesn't ever run (a simple i = 2)

Recommended steps

As the Python Zen itself says that explicit is better than implicit, by using different variable for the result you want to keep after the break you're making this value explicit instead of implicit to the i variable after the loop.

Example:

def thousandthPrime():
    count = 0
    candidate = 5 # candidates for prime no. these are all odd no.s Since starts at 5 therefore we find 998th prime no. as 2 and 3 are already prime no.s
    while(True):
        # print 'Checking =',candidate
        useful_divider = 2
        for i in range(2, candidate / 2): # if any number from 2 to candidate/2 can divide candidate with remainder = 0 then candidate is not a prime no.
            if candidate % i == 0:
                useful_divider = i
                break
            if useful_divider == (candidate / 2) - 1: # If none divide it perfectly, useful_divider will reach candidate/2-1 eventually. So, this is a prime number.
            count+=1
            print 'No. of prime no.s found excluding 2 and 3 =',count, '--->',candidate
        # And so on...
        # ... and so on :)
ssice
  • 3,564
  • 1
  • 26
  • 44