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.