From 56192f1c27d1c4f108f8061a159350b1d36a8f95 Mon Sep 17 00:00:00 2001 From: jeffreypaul15 Date: Thu, 3 Jun 2021 23:53:07 +0530 Subject: [PATCH 1/4] Update example and docstring to encourage the use of functools.partial --- doc/api/animation_api.rst | 34 ++++++++++++++++++++++++++++++++-- lib/matplotlib/animation.py | 3 ++- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/doc/api/animation_api.rst b/doc/api/animation_api.rst index 132590456763..eac200876c2e 100644 --- a/doc/api/animation_api.rst +++ b/doc/api/animation_api.rst @@ -113,6 +113,7 @@ artist at a global scope and let Python sort things out. For example :: import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation + from functools import partial fig, ax = plt.subplots() xdata, ydata = [], [] @@ -133,8 +134,37 @@ artist at a global scope and let Python sort things out. For example :: init_func=init, blit=True) plt.show() -The second method is to use `functools.partial` to 'bind' artists to -function. A third method is to use closures to build up the required +The second method is to use `functools.partial` to pass arguments to the +function. :: + + import numpy as np + import matplotlib.pyplot as plt + from matplotlib.animation import FuncAnimation + from functools import partial + + fig, ax = plt.subplots() + ln, = plt.plot([], [], 'ro') + + def init(): + ax.set_xlim(0, 2*np.pi) + ax.set_ylim(-1, 1) + return ln, + + def update(frame, x, y): + x.append(frame) + y.append(np.sin(frame)) + ln.set_data(xdata, ydata) + return ln, + + xdata, ydata = [], [] + ani = FuncAnimation( + fig, partial(update, x=xdata, y=ydata), + frames=np.linspace(0, 2 * np.pi, 128), + init_func=init, blit=True) + + plt.show() + +A third method is to use closures to build up the required artists and functions. A fourth method is to create a class. Examples diff --git a/lib/matplotlib/animation.py b/lib/matplotlib/animation.py index 428e00904277..ae2f53904e2e 100644 --- a/lib/matplotlib/animation.py +++ b/lib/matplotlib/animation.py @@ -1564,7 +1564,8 @@ def init_func() -> iterable_of_artists value is unused if ``blit == False`` and may be omitted in that case. fargs : tuple or None, optional - Additional arguments to pass to each call to *func*. + Additional arguments to pass to each call to *func*. Note: the use of + `functools.partial` is preferred over *fargs*. save_count : int, default: 100 Fallback for the number of values from *frames* to cache. This is From 179f95e85d07bb4a49f0513f7ee746221eeead3b Mon Sep 17 00:00:00 2001 From: Oscar Gustafsson Date: Fri, 21 Oct 2022 10:23:45 +0200 Subject: [PATCH 2/4] Update docstring --- doc/api/animation_api.rst | 15 +++++++-------- lib/matplotlib/animation.py | 16 ++++++++++++++-- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/doc/api/animation_api.rst b/doc/api/animation_api.rst index eac200876c2e..282e2f1dcd3d 100644 --- a/doc/api/animation_api.rst +++ b/doc/api/animation_api.rst @@ -113,7 +113,6 @@ artist at a global scope and let Python sort things out. For example :: import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation - from functools import partial fig, ax = plt.subplots() xdata, ydata = [], [] @@ -146,15 +145,15 @@ function. :: ln, = plt.plot([], [], 'ro') def init(): - ax.set_xlim(0, 2*np.pi) - ax.set_ylim(-1, 1) - return ln, + ax.set_xlim(0, 2*np.pi) + ax.set_ylim(-1, 1) + return ln, def update(frame, x, y): - x.append(frame) - y.append(np.sin(frame)) - ln.set_data(xdata, ydata) - return ln, + x.append(frame) + y.append(np.sin(frame)) + ln.set_data(xdata, ydata) + return ln, xdata, ydata = [], [] ani = FuncAnimation( diff --git a/lib/matplotlib/animation.py b/lib/matplotlib/animation.py index ae2f53904e2e..409576a126b6 100644 --- a/lib/matplotlib/animation.py +++ b/lib/matplotlib/animation.py @@ -1520,12 +1520,24 @@ class FuncAnimation(TimedAnimation): func : callable The function to call at each frame. The first argument will be the next value in *frames*. Any additional positional - arguments can be supplied via the *fargs* parameter. + arguments can be supplied using `functools.partial` or via the *fargs* + parameter. The required signature is:: def func(frame, *fargs) -> iterable_of_artists + It is often more convenient to provide the arguments using + `functools.partial`. In this way it is also possible to pass keyword + arguments. To pass a function with both positional and keyword + arguments, set all arguments as keyword arguments, just leaving the + *frame* argument unset:: + + def func(frame, x, *, y=None): + ... + + ani = FuncAnimation(fig, partial(func, x=1, y='foo')) + If ``blit == True``, *func* must return an iterable of all artists that were modified or created. This information is used by the blitting algorithm to determine which parts of the figure have to be updated. @@ -1565,7 +1577,7 @@ def init_func() -> iterable_of_artists fargs : tuple or None, optional Additional arguments to pass to each call to *func*. Note: the use of - `functools.partial` is preferred over *fargs*. + `functools.partial` is preferred over *fargs*. See *func* for details. save_count : int, default: 100 Fallback for the number of values from *frames* to cache. This is From cb0a6a0558b7543e6d6914649934a4e768f425f0 Mon Sep 17 00:00:00 2001 From: Oscar Gustafsson Date: Fri, 21 Oct 2022 21:49:58 +0200 Subject: [PATCH 3/4] Apply suggestions from code review Co-authored-by: Thomas A Caswell --- doc/api/animation_api.rst | 6 +++--- lib/matplotlib/animation.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/api/animation_api.rst b/doc/api/animation_api.rst index 282e2f1dcd3d..13b0acf026c2 100644 --- a/doc/api/animation_api.rst +++ b/doc/api/animation_api.rst @@ -142,14 +142,14 @@ function. :: from functools import partial fig, ax = plt.subplots() - ln, = plt.plot([], [], 'ro') + line1, = plt.plot([], [], 'ro') def init(): ax.set_xlim(0, 2*np.pi) ax.set_ylim(-1, 1) return ln, - def update(frame, x, y): + def update(frame, ln, x, y): x.append(frame) y.append(np.sin(frame)) ln.set_data(xdata, ydata) @@ -157,7 +157,7 @@ function. :: xdata, ydata = [], [] ani = FuncAnimation( - fig, partial(update, x=xdata, y=ydata), + fig, partial(update, ln=line1, x=xdata, y=ydata), frames=np.linspace(0, 2 * np.pi, 128), init_func=init, blit=True) diff --git a/lib/matplotlib/animation.py b/lib/matplotlib/animation.py index 409576a126b6..c9084ee7a0a8 100644 --- a/lib/matplotlib/animation.py +++ b/lib/matplotlib/animation.py @@ -1533,10 +1533,10 @@ def func(frame, *fargs) -> iterable_of_artists arguments, set all arguments as keyword arguments, just leaving the *frame* argument unset:: - def func(frame, x, *, y=None): + def func(frame, art, *, y=None): ... - ani = FuncAnimation(fig, partial(func, x=1, y='foo')) + ani = FuncAnimation(fig, partial(func, art=ln, y='foo')) If ``blit == True``, *func* must return an iterable of all artists that were modified or created. This information is used by the blitting From d97cc32411df85c4ce5055829948eb90f49b3656 Mon Sep 17 00:00:00 2001 From: Oscar Gustafsson Date: Sat, 22 Oct 2022 11:07:37 +0200 Subject: [PATCH 4/4] Fix partial example --- doc/api/animation_api.rst | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/doc/api/animation_api.rst b/doc/api/animation_api.rst index 13b0acf026c2..d1b81e20b5c8 100644 --- a/doc/api/animation_api.rst +++ b/doc/api/animation_api.rst @@ -108,7 +108,7 @@ this means that the callable objects you pass in must know what artists they should be working on. There are several approaches to handling this, of varying complexity and encapsulation. The simplest approach, which works quite well in the case of a script, is to define the -artist at a global scope and let Python sort things out. For example :: +artist at a global scope and let Python sort things out. For example:: import numpy as np import matplotlib.pyplot as plt @@ -134,7 +134,7 @@ artist at a global scope and let Python sort things out. For example :: plt.show() The second method is to use `functools.partial` to pass arguments to the -function. :: +function:: import numpy as np import matplotlib.pyplot as plt @@ -142,23 +142,22 @@ function. :: from functools import partial fig, ax = plt.subplots() - line1, = plt.plot([], [], 'ro') + line1, = ax.plot([], [], 'ro') def init(): ax.set_xlim(0, 2*np.pi) ax.set_ylim(-1, 1) - return ln, + return line1, def update(frame, ln, x, y): x.append(frame) y.append(np.sin(frame)) - ln.set_data(xdata, ydata) + ln.set_data(x, y) return ln, - xdata, ydata = [], [] ani = FuncAnimation( - fig, partial(update, ln=line1, x=xdata, y=ydata), - frames=np.linspace(0, 2 * np.pi, 128), + fig, partial(update, ln=line1, x=[], y=[]), + frames=np.linspace(0, 2*np.pi, 128), init_func=init, blit=True) plt.show()