Keegan Keplinger
Keegan Keplinger

Reputation: 627

python animation without globals

I'm writing a Conway's Game of Life implementation. My first attempt was just to plot the board after each update using matplotlib's imshow on a NxN board of 1's and 0's. However, this didn't work as the program pauses whenever it shows the plot. You have to close the plot to get the next loop iteration.

I found out there was an animation package in matplotlib, but it doesn't take (or give) variables, so every implementatin of it I've seen (even matplotlib's documentation) relies on global variables.

So there's kind of two questions here:

1) Is this a place where it's ok to use globals? I've always read that it's never a good idea, but is this just dogma?

2) how would you do such an animation in python without globals (Even if it means ditching matplotlib, I guess; standard library is always preferred).

Upvotes: 5

Views: 3327

Answers (2)

unutbu
unutbu

Reputation: 879919

Here is an example of using FuncAnimation without a custom class:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

def animate(data, im):
    im.set_data(data)

def step():
    while True:
        # replace this with Conway's Game of Life logic
        data = np.random.randn(10, 10)
        yield data

fig, ax = plt.subplots()
im = ax.imshow(np.random.randn(10, 10), interpolation='nearest')
ani = animation.FuncAnimation(
    fig, animate, step, interval=10, repeat=True, fargs=(im, ))
plt.show()

When you use yield (as opposed to return) in a function, it makes the function a generator. Generator functions save state. Each time you call next on the iterator returned by the generator, the flow of execution picks up where it left off (from the last yield expression). This is the why generators are a way to avoid globals -- the would-be globals are just local variables inside the generator function, and their state is saved between calls to next.


By the way, the admonition, "never use globals" is not precise enough. We use globals all the time. Every time you import a module at the module level, the module object is a global. Every time you define a function or class at the module level, it is a global. There is nothing wrong with using globals (although it is true that accessing globals from inside a function is slower than accessing the function's locals. Nevertheless, beware of pre-optimization).

Perhaps instead the admonition should read, "Try never to use globals that change state." The reason why changing global values is bad is because every function that changes the global becomes silently related. The function no longer can be understood and tested as an isolated unit of code. The primary tool programmers use to solve problems is to break big problems down into smaller pieces. Functions and classes help break problems into smaller pieces. But when you use mutating globals, you lose this benefit. The mind must now grok the entire module at once to understand the code.

Upvotes: 1

phihag
phihag

Reputation: 287915

These are just example programs. You can use an object instead of global variables, like this:

class GameOfLife(object):
    def __init__(self, initial):
        self.state = initial
    def step(self):
        # TODO: Game of Life implementation goes here
        # Either assign a new value to self.state, or modify it
    def plot_step(self):
        self.step()
        # TODO: Plot here

# TODO: Initialize matplotlib here
initial = [(0,0), (0,1), (0,2)]
game = GameOfLife(initial)
ani = animation.FuncAnimation(fig, game.plot_step)
plt.show()

If you really want to avoid classes, you can also rewrite the program like this:

def step(state):
    newstate = state[:] # TODO Game of Life implementation goes here
    return newstate
def plot(state):
    # TODO: Plot here
def play_game(state):
    while True:
         yield state
         state = step(state)

initial = [(0,0), (0,1), (0,2)]
game = play_game(initial)
ani = animation.FuncAnimation(fig, lambda: next(game))
plt.show()

Note that for non-mathematical animations (without labels, graphs, scales, etc.), you may prefer pyglet or pygame.

Upvotes: 5

Related Questions