是否可以自动生成带参数的函数?

Is it possible to autogenerate functions with arguments?

我有一个方法可以根据形状的名称和一些参数绘制一条线或任何我需要的形状的补丁,它工作正常:

def draw_a_shape(ax, is_patch_not_line, shapename, **kwargs):
  ...

现在我需要30多个函数,比如draw_a_square_patchdraw_an_ellipse_outline等。我可以用一种简单的方式实现它们,如下所示,但是有点痛苦:

def draw_a_square_patch(ax, **kwargs):
  draw_a_shape(ax=ax, is_patch_not_line=True, shapename='a_square', **kwargs)
def draw_a_square_outline(ax, **kwargs):
  draw_a_shape(ax=ax, is_patch_not_line=False, shapename='a_square', **kwargs)
def draw_an_ellipse_patch(ax, **kwargs):
  draw_a_shape(ax=ax, is_patch_not_line=True, shapename='an_ellipse', **kwargs)
def draw_an_ellipse_outline(ax, **kwargs):
  draw_a_shape(ax=ax, is_patch_not_line=False, shapename='an_ellipse', **kwargs)
.........

我想知道是否有一种方法可以使用 循环 来实现,或许可以使用 setattr 之类的方法?我在下面添加了一个无效代码,只是为了解释我的意思:

for shapename in ['a_circle', 'a_square', ...]:
  for is_patch_not_line in [False, True]:
    new_func_name = "draw_" + shapename + "_"
    if is_patch_not_line:
      new_func_name += "patch"
    else:
      new_func_name += "outline"
    global.setattr(new_func_name, "ax, **kwargs", ...) 

以下似乎可行。我必须创建 make_new_draw_a_shape 来包装内部函数,因为直接在 for 循环中创建内部函数会导致生成的最后一个函数分配给所有名称。

def draw_a_shape(ax, is_patch_not_line, shapename, **kwargs):
    print(f"is_patch_not_line={is_patch_not_line}, shapename={shapename}")


def make_new_draw_a_shape(is_patch_not_line, shapename):
    def new_draw_a_shape(ax, **kwargs):
        draw_a_shape(ax, is_patch_not_line, shapename, **kwargs)
    return new_draw_a_shape


for shapename in ['a_circle', 'a_square', 'an_ellipse']:
    for is_patch_not_line in [False, True]:
        new_func_name = "draw_" + shapename + "_"
        if is_patch_not_line:
            new_func_name += "patch"
        else:
            new_func_name += "outline"
        globals()[new_func_name] = make_new_draw_a_shape(is_patch_not_line, shapename)


draw_a_circle_patch(None)
draw_a_circle_outline(None)
draw_a_square_patch(None)
draw_a_square_outline(None)
draw_an_ellipse_patch(None)
draw_an_ellipse_outline(None)

输出:

is_patch_not_line=True, shapename=a_circle
is_patch_not_line=False, shapename=a_circle
is_patch_not_line=True, shapename=a_square
is_patch_not_line=False, shapename=a_square
is_patch_not_line=True, shapename=an_ellipse
is_patch_not_line=False, shapename=an_ellipse

functools.partial 可用于创建部分对象,其行为类似于已传递一些参数的其他函数。这些可以通过 locals() 字典在本地命名空间中或通过 globals() 字典在模块命名空间中给出动态生成的名称(关于字典和其他结构的标准警告有利于 globals() 在大多数用例中,除了这个问题中的特定用例)。

把它们放在一起会给你这样的东西:

for shapename in ['a_circle', 'a_square', ...]:
  for is_patch_not_line in [False, True]:
    new_func_name = "draw_" + shapename + "_"
    if is_patch_not_line:
      new_func_name += "patch"
    else:
      new_func_name += "outline"
    globals()[new_func_name] = functools.partial(
      draw_a_shape,
      shapename=shapename,
      is_patch_not_line=is_patch_not_line,
    )