用高阶函数封装matplotlib双轴绘图逻辑,该设计模式是什么?有Python语法糖吗?
Great question! Let's break this down clearly:
First, this isn't a decorator pattern—it's actually the Strategy Pattern, a classic behavioral design pattern.
What's the Strategy Pattern here?
The core idea of the Strategy Pattern is to define a family of algorithms (or in your case, rendering functions like A, B, C), encapsulate each one, and make them interchangeable. Your plot function acts as the "context" that accepts these strategies (the functions f1/f2) and executes them against the axes objects, without needing to know the specifics of how each function modifies the axes.
In your code:
- Strategies:
A,B,C(each encapsulates a different way to modify anaxobject) - Context: The
plotfunction (handles the common repetitive logic, and delegates the actual rendering to the strategies you pass in)
Why isn't this a decorator?
Decorators are used to wrap existing functions to add or modify their behavior (e.g., adding logging, timing, or input validation) while keeping the original function's interface intact. Your plot function isn't wrapping A/B/C—it's treating them as interchangeable tools to execute a specific task (modifying axes), which is the heart of the Strategy Pattern.
Python syntax sugar to simplify this
You're already using *args to handle variable-length arguments, which is perfect, but there are a few more Pythonic touches you can add:
Support keyword arguments with
**kwargs
Modify yourplotfunction to accept keyword arguments for the strategies, making it more flexible if your functions ever need named params:def plot(ax1, f1, args1=None, kwargs1=None, ax2=None, f2=None, args2=None, kwargs2=None): args1 = args1 or () kwargs1 = kwargs1 or {} ax1 = f1(ax1, *args1, **kwargs1) if ax2 and f2: args2 = args2 or () kwargs2 = kwargs2 or {} ax2 = f2(ax2, *args2, **kwargs2) return ax1, ax2Use
functools.partialto pre-bind arguments
If you find yourself reusing the same function with fixed parameters, you can pre-bind them usingfunctools.partialto make calls cleaner:from functools import partial if __name__=="__main__": x = np.linspace(0,10,10) # Pre-bind params for A and C plot_A = partial(A, x=x, a=1, b=2, c=3) plot_C = partial(C, n=5) fig, (ax1, ax2) = plt.subplots(2, 1) ax1, ax2 = plot(ax1, plot_A, (), {}, ax2, plot_C, (), {}) plt.show()Fix the typo in function
C
Quick note: YourCfunction currently returnsxinstead ofax—that's probably a mistake, since all other strategies return the modified axis object.
Bonus: Make the plot function more self-contained
If you want to reduce even more boilerplate, you could have the plot function create the figure and axes for you, instead of requiring them as inputs:
def plot(f1, args1, f2, args2): fig, (ax1, ax2) = plt.subplots(2, 1) ax1 = f1(ax1, *args1) ax2 = f2(ax2, *args2) return fig, ax1, ax2 # Usage: fig, ax1, ax2 = plot(A, (x, 1, 2, 3), C, (5,)) plt.show()
That way you don't have to repeat plt.subplots(2, 1) every time!
内容的提问来源于stack exchange,提问作者user32882

