tags:

views:

66

answers:

4

I have a method in a Pygame Sprite subclass, defined as such:

def walk(self):
    """move across screen"""
    displacement = self.rect.move((self.move, 0))
    if self.rect.left < self.area.left or self.rect.right > self.area.right:
        self.move = -self.move
        displacement = self.rect.move((self.move, 0))
    self.rect = displacement

I modified it, adding a parameter speed_x, and now the program is broken.

def walk(self, speed_x):
    """move across screen"""
    displacement = self.rect.move((speed_x, 0))
    if self.rect.left < self.area.left or self.rect.right > self.area.right:
        speed_x = -speed_x
        displacement = self.rect.move((speed_x, 0))
    self.rect = displacement

Before I called the method like this:

def update(self):
        self.walk()

Now I do:

def update(self):
    self.walk(self.move)

Why doesn't this work?

+3  A: 

You don't explain how it's "broken", but the main difference is that

speed_x = -speed_x

which you have in your second version, is only changing the local variable (arguments are local variables!) speed_x, so that changed value does not persist.

In the first version,

self.move = -self.move 

does alter self (specifically one of its attriubtes) and the alteration "persists" in future method calls on the object which is here accessed as self.

Just one of the many key differences between bare names (like speed_x) and qualified names (line self.move), and, I suspect, what's biting you here (hard as you may make it to guess by not saying how the second version is failing your expectations).

Alex Martelli
Ah, that makes sense. Thanks. Instead of changing direction each time the object (an image) hit the edge of the screen, the image would just get stuck on that side. That's how it was broken.
SteveStifler
@Steve, you're welcome, always glad to be of assistance!
Alex Martelli
+1  A: 

You are no storing the offset back in to self.move.

+1  A: 

If you want to use the second version of your code, try adding this line:

    self.move = speed_x

At the bottom of your function.

Xavier Ho
+1  A: 

As mentioned by others, you are not changing the value of self.move in your new code. I assume the reason you modified this function was so you could reuse this function for values other than self.move.

If you want to be able to pass different arguments into your function and modify them as well, you could pass the modified value of speed_x back as a return value:

def walk(self, speed_x):
    """move across screen"""
    displacement = self.rect.move((speed_x, 0))
    if self.rect.left < self.area.left or self.rect.right > self.area.right:
        speed_x = -speed_x
        displacement = self.rect.move((speed_x, 0))
    self.rect = displacement
    return speed_x

And call the function like this as:

def update(self):
    self.move = self.walk(self.move)

Note: This answer assumes that self.move should not always be updated when calling walk. If this assumption is false and self.move should in fact be updated every time walk is run, then you should instead use Xavier Ho's answer.

Trey
I'm not sure if I'm seeing a brilliant design, or a horrifying design. Why would you return the value passed in, and then have to assign the parameter to itself?
Xavier Ho
I am basing this answer on the assumption that this function will be used for values other than `self.move` and that one of the jobs of this function is to update the value of the passed in argument. Since updating the value of an immutable argument is not possible in Python, the argument needs to be updated outside of the method. My answer is completely inappropriate if `self.move` should in fact be updated no matter what the second argument is to `walk` (as I believe your answer assumes).
Trey