2

I'm not sure if the title gave the best description. But this here is my problem. I have a class named 'Ball'. Each ball has its own width, radius and color. My code worked great while I was adding my own balls before the loop eg. ball1 = Ball() .... ball2 = Ball() I'm using pygame and what I want is so that whenever I press 'Space' it adds another ball with it's own characteristics. I have it so that it randomly gives a width, radius and color. Here is my code:

BLACK = (0,0,0)
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)

fps = 30

color_list = [BLACK, BLUE, GREEN, RED]

col = None
siz = None
wit = None
posx = None
posy = None
ball = None


class Ball:

    ballCount = 0

    def __init__(self):
        Ball.ballCount +=1
        self.col = random.choice(color_list)
        self.siz = random.randint(20, 80)
        self.wit = random.randint(1, 3)
        self.posx = random.randint(self.siz, width-self.siz)
        self.posy = random.randint(self.siz, height-self.siz)

    def blitball(self):
        pygame.draw.circle(screen, self.col, (self.posx, self.posy),self.siz, self.wit)


    def move(self):
        self.posx+=1



ball2 = Ball()
ball1 = Ball()
ball3 = Ball()

while True:

    amount = 0



    event = pygame.event.poll()
    keys = pygame.key.get_pressed()

    if event.type == QUIT:
        pygame.quit()
        sys.exit()

    if keys[K_q]:
        pygame.quit()
        sys.exit()

    #############################################################

    if keys[K_SPACE]:
        eval("ball"+str(Ball.ballCount+1)) = Ball()

    #############################################################

    screen.fill(WHITE)

    for r in range(Ball.ballCount):
        amount+=1
        eval("ball"+str(amount)).move()
        eval("ball"+str(amount)).blitball()


    pygame.time.wait(int(1000/fps))

    pygame.display.update()

The for r in range(Ball.ballCount): is used so I don't have to type the functions for every ball. This works perfectly. If you know an easier way let me know.

So what I need to add is:

if keys[K_SPACE]:  
    #add another ball, ball3, ball4,..etc. 

If this means changing some of my code please feel free to tell me or even do so yourself. Thanks for the replies in advance. (I HAVE MY PROBLEM WITHIN THE HASHTAGS)

Dennis

2
  • 1
    You already use lists, so you should know them. Why not use them for this as well? Commented Feb 15, 2013 at 18:20
  • Seems like more of a design issue here - you're trying to make Balls do two things - represent a Ball object but also keep track of all the other ball objects. Seems like your design would be much simpler if you handle them as two separate concerns; a list of balls and the balls themselves. Commented Feb 15, 2013 at 18:23

2 Answers 2

3

Store your balls in a list and act on them there

# starting three balls
balls = [Ball(),Ball(),Ball()]

while True:
    amount = 0

    event = pygame.event.poll()
    keys = pygame.key.get_pressed()

    if event.type == QUIT:
        pygame.quit()
        sys.exit()

    if keys[K_q]:
        pygame.quit()
        sys.exit()

    #############################################################

    if keys[K_SPACE]:
        balls.append(Ball())

    #############################################################

    screen.fill(WHITE)

    for ball_in_play in balls:
        ball_in_play.move()
        ball_in_play.blitball()


    pygame.time.wait(int(1000/fps))

    pygame.display.update()
Sign up to request clarification or add additional context in comments.

2 Comments

+1 for showing the code, including why the list makes the loop so much nicer.
Thanks a million, I'll do that from now on worked perfectly !!
2

You almost never want to create a new variable by name.

And, even on the rare occasions when you do, you almost never want to do it with eval or exec.

And, even if you do want to use eval or exec, you almost never want to use them with the default locals/globals.

But, let's look at why your code isn't working first, then how to do it better.

eval("ball"+str(Ball.ballCount+1)) = Ball()

You're generating a string like "ball4", then calling eval on that. Not surprisingly, that evaluates to the string "ball4". Then you're trying to assign a new value (the newly-constructed Ball instance) to that string, instead of to a variable, and obviously that won't work. (Actually, I'm guessing you'd get a SyntaxError for trying to assign to a function call, before anything even gets evaluated.)

To fix that, you have to put the whole thing inside eval:

eval("ball"+str(Ball.ballCount+1) + " = Ball()")

But that won't work, because eval only works for expressions, not statements. For statements, you need exec:

exec("ball"+str(Ball.ballCount+1) + " = Ball()")

And that will get rid of your errors.

But let's look at the right way to create the ball4 variable:

locals()["ball" + str(Ball.ballCount+1)] = Ball()

A lot simpler and harder to get wrong.

But once you see that, why bother using locals() as a dict when you can use any dict with any name you want?

my_balls["ball" + str(Ball.ballCount+1)] = Ball()

And, once you've done that, you don't need the "ball" prefix on each one:

my_balls[Ball.ballCount+1] = Ball()

And at that point, you may realize that your keys are just the natural numbers, which means you're just simulating a list with a dict, so you might as well just make my_balls a list.

And then, you don't need Ball.ballCount anymore—it's just len(my_balls).

So, what's so bad about using eval and exec?

Well, it's less efficient, it makes your code harder to debug (and to experiment with in the interactive terminal), and it opens huge security holes (imagine that you asked a user for the next ball number, and he gave you back "3; os.system('rm -rf /'); _ " and you then tried to exec("Ball" + user_input_string + " = Ball()")).

But all of that is nothing compared to the fact that it makes your code much harder to read. Compare:

eval("ball"+str(amount)).move()

balls[amount].move()

1 Comment

Thanks so much for the answer. Although I thought Paul Seeb showed a brilliant way to do exactly what I wanted to do, I'm delighted now that I understand Eval and Exec a lot better now and for some of my future programs, this answer should be of great help to me. I chose Paul Seebs answer as that's what I'll be using in my code but your's was just as helpful and it was a tough decision. Hugely appreciated.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.