0

I am just wondering if there is a solution to pass a function (fn2) as an argument (of fn1), and this function would use the internal parameters of the initial function (fn1)

For example:

def fn2(a: str):
    print("fn2 string print", a)

def fn1(fn()):
    b = "fn1 string going to pass to fn2"
    print(fn(b))

This example is kinda stupid but it illustrate I think what I have in mind. I completely understand that in the current way it's written it would not work at all but is there a way to do that ?

For a more real example, I am working on creating a "general plotting function" that would take axes from other functions and add them to a final figure such as:

colors = {
    "royalblue": (25, 41, 88),
    "pink": (255, 192, 203),
    "darkorange": (100, 55, 0),
    "indianred": (205, 92, 92),
    "forestgreen": (34, 139, 34),
    "dimgrey": (105, 105, 105),
}

def ax_regions(
    sgm_dict: dict,
    ax: plt.Axes,
) -> plt.Axes:
    """
    Plot the regions for a dictionary of regions (keys)
    and coordinates (values)

    Args:
        - sgm_dict (dict): segment dictionary with region and coordinates
        - ax (plt.Axes): axes to write onto

    """
    for idx, (sname, scoords) in enumerate(sgm_dict.items()):
        ax.plot(
            scoords[:, 0], scoords[:, 1], label=sname, color=list(colors.keys())[idx]
        )
    return ax

def frame_region_plot(
    frame: np.ndarray,
    *data,
    **fn,
    title: str = "Plot",
    fname: Union[str, bool] = False,
    savedir: Union[str, bool] = False,
):
    """Regions plot

    Args:
        - frame (np.ndarray): image to overlay the region onto
        - title (str): title of the plot
        - fname (Union[str, bool], optional): File name when saving. Defaults to False.
        - savedir (Union[str, bool], optional): Saving directory path. Defaults to False.
    """
    fig, ax = plt.subplots()
    plt.imshow(frame, cmap="bone")

    for d in data:
        fig.axes.append(fn(d, ax))

    plt.title(title)

    handles, labels = plt.gca().get_legend_handles_labels()
    by_label = dict(zip(labels, handles))
    plt.legend(by_label.values(), by_label.keys())

    plt.tight_layout()

    if savedir:
        savedir = os.path.join(savedir, " ")
        plt.savefig(f"{savedir}{fname}.png")
    plt.show()

In this example, I'd like to pass the ax_regions() function as an argument to the frame_region_plot() function.

How would that be possible ?

Unic0
  • 341
  • 1
  • 3
  • 19
  • 1
    Yes, you can do that, but you need to pass the function *object*, not the *return value*. So, for your first example, if would be `def fn1(fn):...`. As for your "more real" example, I don't see any code that demonstrates function passing - you're simply calling `ax_regions()` from within `frame_region_plot()`. You don't need to pass the one function object to the other, they exist within the same namespace. – MattDMo Aug 24 '22 at 00:00
  • I edited my question to, hopefully, make it clearer on the 'real world example'. I'd like to pass any kind of function that'll return an ax to be plotted in the figure created in the `frame_region_plot` – Unic0 Aug 24 '22 at 00:09
  • Don't use `**fn` in the function signature, for one thing, just the argument `fn` will suffice. `**` indicates unpacking keyword arguments, and doesn't apply to function objects. – MattDMo Aug 24 '22 at 00:15
  • Noted ! Thank you very much ! I didn't expect it to be so simple ! Thanks again – Unic0 Aug 24 '22 at 00:18
  • If you really want to go down a rabbit hole, you can look up python decorators~ – BeRT2me Aug 24 '22 at 00:27

1 Answers1

2

I'm not sure I have understood what you want. For example this:

def fn2(a: str):
    print("fn2 string print", a)

def fn1(func):
    b = "fn1 string going to pass to fn2"
    func(b)

fn1(fn2)

Outputs this: fn2 string print fn1 string going to pass to fn2

If this is what you want, you can simply call the function fn2 inside fn1

M3tex
  • 83
  • 6
  • Indeed it's what I wanted, I didn't know that it was possible to pass a function without giving it arguments ! But it does work ! Thank you ! – Unic0 Aug 24 '22 at 00:17
  • Python takes "everything is an object" seriously. "Object" is just a fancy word for "thing", so that doesn't sound like it tells you anything; but what it means is: if it has a name, then syntactically you can use it anywhere that you could use a name. So, a function can be passed as an argument, or returned, or assigned to another name, or have attributes, or be put in a list... and while functions don't happen to support e.g. arithmetic operators or slicing, that's a *semantic* limitation that's only detected at runtime. – Karl Knechtel Aug 24 '22 at 00:24