animation with FuncAnimation (mpl 3.0.2)

I think I found a bug, and I have a question.
Consider the code below.

1. Although frames=10, `animate` is called 11 times, twice with an argument of 0.
(The `print` calls show this.) I suspect this is a bug?

2. Why is the assignment of the FuncAnimation instance to a name
required for the animation to work, and exactly how does this trick work?
How could I change this example to avoid this hidden magic?

Thank you,
Alan Isaac

import random
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

fig, ax = plt.subplots()
xs, ys = [],[]
l1, = ax.plot(xs, ys, 'b.')
ax.set_xlim(0,10)
ax.set_ylim(0,10)

def animate(i):
     print(i)
     xs.append(random.random()*10)
     ys.append(random.random()*10)
     l1.set_data(xs, ys)

ani = FuncAnimation(fig, animate, frames=10, interval=200, repeat=False)
plt.show()

I think I found a bug, and I have a question.
Consider the code below.

1. Although frames=10, `animate` is called 11 times, twice with an
argument of 0.
(The `print` calls show this.) I suspect this is a bug?

It's not a bug--it's intentional and documented--but I agree it is not
what one might expect. The problem is that in general animations
require an initialization followed by generation of the frames. If no
"init_func" argument is supplied for the initialization, the
frame-generator (your animate() function) is used, leading to the double
call. Since you are doing your initialization in the body of the
script, I think you could eliminate the double call to animate(0) by
supplying a do-nothing init_func argument to FuncAnimation.

2. Why is the assignment of the FuncAnimation instance to a name
required for the animation to work, and exactly how does this trick work?
How could I change this example to avoid this hidden magic?

This is a basic aspect of the Python language: when the last reference
to an object is removed, the object goes away. Assigning a name to the
FuncAnimation instance keeps it alive so that it can do its work when
the blocking "plt.show()" is called.

Eric

···

On 2019/03/22 9:03 AM, Alan Isaac wrote:

Thank you,
Alan Isaac

import random
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

fig, ax = plt.subplots()
xs, ys = [],[]
l1, = ax.plot(xs, ys, 'b.')
ax.set_xlim(0,10)
ax.set_ylim(0,10)

def animate(i):
??? print(i)
??? xs.append(random.random()*10)
??? ys.append(random.random()*10)
??? l1.set_data(xs, ys)

ani = FuncAnimation(fig, animate, frames=10, interval=200, repeat=False)
plt.show()
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users at python.org
https://mail.python.org/mailman/listinfo/matplotlib-users

It's not a bug--it's intentional and documented--but I agree it is not what one might expect.

Oh I see, it is documented under `init_func`. Thanks!
Perhaps it shd also be documented under `func`. (That wd have helped me.)

This is a basic aspect of the Python language: when the last reference to an object is removed, the object goes away.? Assigning a name to the FuncAnimation instance keeps it alive so that it can do its work when the?blocking?"plt.show()"?is?called.

This approach seems to depend sensitively on the timing of garbage collection.
Is it part of the Python specification that a variable created in a scope
will ensure the object's existence until the scope is exited, even if the
variable is unused? Is that guaranteed to work regardless of the Python
implementation? (I searched but did not find an answer to this question.)

And it still seems like magic to me: how is matplotlib finding the object?
For example, if mpl registered the object upon creation, it seems like the
assignment would not matter. But the assignment does matter,
so some other approach is used.

Thanks!
Alan

···

On 3/22/2019 3:41 PM, Eric Firing wrote:

It's not a bug--it's intentional and documented--but I agree it is not
what one might expect.

Oh I see, it is documented under `init_func`.? Thanks!
Perhaps it shd also be documented under `func`.? (That wd have helped me.)

This is a basic aspect of the Python language: when the last reference
to an object is removed, the object goes away.? Assigning a name to
the FuncAnimation instance keeps it alive so that it can do its work
when the?blocking?"plt.show()"?is?called.

This approach seems to depend sensitively on the timing of garbage
collection.

It doesn't, because as long as there is a reference to an object, and it
is not a cyclic reference in which there are no references from outside
to objects in the cycle, the object will remain alive. This is the
original Python method for deciding when to delete an object and reclaim
its memory: reference counting. The object is deleted immediately when
its reference count drops to zero.

In Python 2, garbage collection was added to handle the problem of
reference cycles, supplementing, but by no means replacing, the
reference-counting method.

In the case of your script, by naming the FuncAnimation instance, you
are creating a reference that lasts until the script exits.

Is it part of the Python specification that a variable created in a scope
will ensure the object's existence until the scope is exited, even if the
variable is unused?? Is that guaranteed to work regardless of the Python
implementation? (I searched but did not find an answer to this question.)

It's not a matter of the scope in which an object was created, but of
the existence of references. If references exist only within a given
scope (e.g., name assignments within a function) then those references,
and therefore the object, go away when the scope is exited.

And it still seems like magic to me: how is matplotlib finding the object?
For example, if mpl registered the object upon creation, it seems like the
assignment would not matter.? But the assignment does matter,
so some other approach is used.

The purpose of the assignment is just to keep the object alive. The
object is using the GUI event loop to trigger events via a timer in the
loop. Calling plt.show() starts the loop running--but if in the interim
the FuncAnimation object has been deleted because there is no reference
to it, then that loop is trying to call functions that no longer exist.

Eric

···

On 2019/03/22 10:08 AM, Alan Isaac wrote:

On 3/22/2019 3:41 PM, Eric Firing wrote:

Thanks!
Alan
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users at python.org
https://mail.python.org/mailman/listinfo/matplotlib-users