12

when trying to compile the code below I get this error

UnboundLocalError: local variable 'L' referenced before assignment

Can someone explain why ? Isn't a global variable assigned before anything else?

My Python version is 2.7.3

#!/usr/bin/env python

import pygame
from pygame.locals import *
from sys import exit
import random
import math

R = int(8)  # promien planety
N = 5  # liczba planet
G = 2  # stala "grawitacyjna"
L = 1

def compute_dv(p1,p2):
    dx = p2[0]-p1[0]
    dy = p2[1]-p1[1]
    r = math.hypot(dx,dy)
    dx /= r*r
    dy /= r*r
    if(L>1000):
   print "r= ", r, "dx= ", dx, "dy= ", dy, "dx/ r*r = ", dx, "dy/ r*r = ", dy
    L+=1
    return G*dx,G*dy


def rand_color():
    r = 32*random.randint(0,7)
    g = 32*random.randint(0,7)
    b = 22*random.randint(0,7)
    return (r,g,b)


pygame.init()
screen = pygame.display.set_mode((640, 480), 0, 32)

points = []
vs = []
colors = []

for i in range(N):
    points.append( [random.randint(0,639), random.randint(0,480)] )
    vs.append( [0,0] )
    colors.append( rand_color() )

clock = pygame.time.Clock()

screen.fill( (255,255,255))

while True:
    clock.tick(30)

for event in pygame.event.get():
    if event.type == QUIT:
        exit()

for i in range(len(points)):
   for j in range(len(points)):
      if points[i]!=points[j]:
         dvx,dvy = compute_dv( points[i],points[j])
         vs[i][0] += dvx
         vs[i][1] += dvy

for i in range(len(points)):
    points[i][0] += vs[i][0]
    points[i][1] += vs[i][1]

screen.fill( (255,255,255))

for i in range(len(points)):
  L = []
  for w in points[i]:
print int(round(w))
L.append(int(round(w)))
  points[i] = L
  print points[i], "stop"
  #x = raw_input()

  pygame.draw.circle(screen, colors[i], points[i], R)  

pygame.display.update()  
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
lvi
  • 169
  • 1
  • 2
  • 8

3 Answers3

28

The minimal code to reproduce your bug is

x = 1
def foo():
    x += 1
foo()

This is happening for a number of reasons

  1. First - because in python we have mutable and immutable classes. Ints are immutable, that is when you write x+=1 you actually create another object (which is not true for certain ints due to optimisations CPython does). What actually happens is x = x + 1.
  2. Second - because python compiler checks every assignment made inside a scope and makes every variable assigned inside that scope local to it.
  3. So as you see when you try to increment x compiler has to access a variable that's local to that scope, but was never assigned a value before.

If you're using python2 - you only have the option to declare variable global. But this way you would be unable to get a variable from an in-between function like

x = 0
def foo():
  x = 1
  def bar():
    global x
    print x  # prints 0
  bar()
foo()    

In python3 you have nonlocal keyword to address this situation.

Also I would advise you to avoid using globals. Also there is a collection.Counter class that might be useful to you.

Further reading: python docs

Kirill Zaitsev
  • 4,511
  • 3
  • 21
  • 29
3

Isn't a global variable assigned before anything else?

Yes, but that's completely irrelevant. The compiler sees an assignment within the function and marks the name as being in the local scope. You need to use the global keyword at the beginning of the function to tell the compiler that the name should be in the global scope instead.

def compute_dv(p1,p2):
    global L
     ...
Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
  • Or, better, redesign to not use a global variable at all, since it's a really terrible design. – Wooble Jan 30 '14 at 12:42
2

You are mixing tabs and spaces; don't do that.

Run your script with python -tt yourscript.py and fix all errors that finds.

Then configure your editor to stick to only spaces for indentation; using 4 spaces per indent is the recommended style by the Python Style Guide.

Next, you are trying to increment the global L here:

def compute_dv(p1,p2):
    # ...

    if(L>1000):
        print "r= ", r, "dx= ", dx, "dy= ", dy, "dx/ r*r = ", dx, "dy/ r*r = ", dy
        L+=1

without declaring it a global. Add global L in that function. Assignment to a name inside a function marks such a name as a local, unless you specifically tell Python it is not.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • @Wooble: Yup, you are correct; I missed the `L+=1` in the function there. The indentation in the post was terrible and there were actual tabs on the later lines with `L` and a `for` loop. – Martijn Pieters Jan 30 '14 at 12:42
  • Thank you for that. I fixed 2 mistakes but didn't solve my problem. – lvi Jan 30 '14 at 12:42