2

I keep having the error

UnboundLocalError: local variable 'new_speedDx' referenced before assignment

while trying to run the following function:

def new_speedD(boid1):
    bposx = boid1[0]
    if bposx < WALL:
        new_speedDx = WALL_FORCE
    elif bposx > WIDTH - WALL:
        new_speedDx = -WALL_FORCE

    bposy = boid1[1]
    if bposy < WALL:
        new_speedDy = WALL_FORCE
    elif bposx > WIDTH - WALL:
        new_speedDy = -WALL_FORCE

    return new_speedDx, new_speedDy

In this function, boid1 is a vector with 4 elements (xpos, ypos, xvelocity, yvelocity) and all variables in upper case are constants (numbers). Anyone got an idea how to solve this? I've found many possible solutions on the internet but nothing seemed to work..

Brian Cain
  • 14,403
  • 3
  • 50
  • 88
user2089012
  • 101
  • 1
  • 8
  • 2
    Note that `bposx = boid1[0]` and `bposy = boid1[1]` can be written more nicely using unpacking: `bposx, bposy = boid1`. – Gareth Latty Mar 19 '13 at 18:16
  • And what about the other two elements? – user2089012 Mar 19 '13 at 18:26
  • If it's a larger tuple, in 3.x you can do `bposx, bposy, *_ = boid1`, which will assign any extra values as a list to `_` (commonly used to indicate a value being discarded), in earlier versions, simply add as many `_`s as there will be values in `boid1` - for example, with four elements: `bposx, bposy, _, _ = boid1`. Naturally, you could use real names, but if you are not using them, `_`s make it clear that was intentional. – Gareth Latty Mar 19 '13 at 18:30
  • 1
    @Lattyware: I'll bet that xvelocity and yvelocity are useful in this function, so unpack them, too. – hughdbrown Mar 19 '13 at 18:31
  • @hughdbrown I see no reason to believe this isn't a complete function. – Gareth Latty Mar 19 '13 at 18:32
  • The velocity isn't important here, because the speed change is only a function of the vector's position. – user2089012 Mar 19 '13 at 18:45

3 Answers3

5

It must be possible that bposx is neither less than WALL nor greater than WIDTH - WALL.

eg:

bposx = 10
WALL = 9
WIDTH = 200

if bposx < WALL:    # 10 is greater than 9, does not define new_speedDx 
    new_speedDx = WALL_FORCE
elif bposx > WIDTH - WALL:   # 10 is less than (200 - 9), does not define new_speedDx
    new_speedDx = -WALL_FORCE

Without seeing the rest of your program it's hard to suggest a reasonable fallback value, but you likely want to add something like:

else:
    new_speedDx = 0
uptownnickbrown
  • 987
  • 7
  • 22
  • 1
    Great. You may have done this already, but I'd note that you may want to do the same for new_speedDy, since it is vulnerable to the same issue. – uptownnickbrown Mar 19 '13 at 18:34
4

What happens if neither of these conditions are true?

if bposx < WALL:
    new_speedDx = WALL_FORCE
elif bposx > WIDTH - WALL:
    new_speedDx = -WALL_FORCE

... new_speedDx is never assigned and so its value is indeterminate.

You can mitigate this by specifying what new_speedDx should be in this case:

if bposx < WALL:
    new_speedDx = WALL_FORCE
elif bposx > WIDTH - WALL:
    new_speedDx = -WALL_FORCE
else:
    new_speedDx = 0.
Brian Cain
  • 14,403
  • 3
  • 50
  • 88
2

Explanation

As others have pointed out, you are not dealing with the case that WALL <= pos <= WIDTH - WALL.

Recommended change

Presumably the boid continues with current velocity if it does not hit a wall. Others have code that sets velocity to 0 if the boid is not hitting a wall. This solution is distinctive in using the existing velocity. I think that is important to your situation.

Code

def new_speedD(boid1):
    def new_speed(pos, velocity):
        return WALL_FORCE if pos < WALL \
            else (-WALL_FORCE if pos > WIDTH - WALL \
            else velocity)
    xpos, ypos, xvelocity, yvelocity = boid1
    new_speedDx = new_speed(posx, xvelocity)
    new_speedDy = new_speed(posy, yvelocity)
    return new_speedDx, new_speedDy

Some think this code is hard to understand. Here is a brief explanation:

  1. return WALL_FORCE if pos < WALL
  2. otherwise, return -WALL_FORCE if pos > WIDTH - WALL
  3. otherwise, return velocity

Here is a general question on the ternary operator. Remember, thought, that "It's frowned upon by some pythonistas."

If you don't use this code...

Return to your original and fix the typo in yvelocity case: bposx > WIDTH - WALL. The yvelocity does not depend on the xpos.

Community
  • 1
  • 1
hughdbrown
  • 47,733
  • 20
  • 85
  • 108
  • I really don't see the benefit of making the `if` statements into ternary operators - it just makes the lines too long and hard to read. – Gareth Latty Mar 19 '13 at 18:31
  • @Lattyware: well, for one thing, it's trivial to pick out that there is a typo in the original code: `bposy = boid1[1] if bposy < WALL: new_speedDy = WALL_FORCE elif bposx > WIDTH - WALL: new_speedDy = -WALL_FORCE` See the check on bposx in the y-case? Obvious in the parallel construction for x and y using the one-line ternary operator. – hughdbrown Mar 19 '13 at 18:34
  • And that's made easier by dumping it all on one line? I completely disagree - one-lining those makes it an unreadable mess. If nothing else, PEP-8 suggests lines should never go beyond 79 characters wide. – Gareth Latty Mar 19 '13 at 18:37
  • Fair enough. Wrapped lines. Put into a function. – hughdbrown Mar 19 '13 at 18:40
  • 1
    My point remains - is that really more readable than just using normal `if` statements? – Gareth Latty Mar 19 '13 at 18:41
  • @Lattyware: It reads pretty simply: "return WALL_FORCE if pos < WALL; [otherwise return] -WALL_FORCE if pos > WIDTH - WALL; [otherwise return] velocity." – hughdbrown Mar 19 '13 at 19:06
  • 1
    Yes, but so does a normal `if` statement, and the normal `if` doesn't get crammed onto one line. – Gareth Latty Mar 19 '13 at 19:07
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/26489/discussion-between-hughdbrown-and-lattyware) – hughdbrown Mar 19 '13 at 19:13