是否可以创建一个 returns 在创建时而不是在调用函数时生成静态值的函数?

Is it possible to create a function that returns a static value generated upon creation, not upon calling the function?

问题陈述:

如何创建一个函数来传递在创建函数时确定的值,而不是在调用函数时确定的值?


背景

使用 altair I am trying to set up multiple themes, and in order to create a theme, I need to "register" 代码如下:

def black_marks():
    return {
        'config': {
            'mark': {
                'color': 'black',
                'fill': 'black'
            }
        }
    }

# register the custom theme under a chosen name
alt.themes.register('black_marks', black_marks)

# enable the newly registered theme
alt.themes.enable('black_marks')

此时,由于 'black_marks'registerdenabled,任何以后使用 altair将包括这些默认值。

注意函数black_marksreturn是一个dict,它是需要注册的函数


问题

我有几个主题想同时设置,所以我在我的代码中通过这些配置创建了一个循环:

for theme_name, theme_dict in mythemes.items():
    themes.register(theme_name, lambda: theme_dict)

然后我发现在调用themes.register函数时实际上没有注册主题。相反,它是“懒惰地”处理的。

例如,假设主题键是:['light', 'dark', 'paper']。完成上面的循环后,我发现所有 3 个主题名称都已 注册 (例如,我可以 alt.themes.enable('light')),但 它们都是指向最后一个,paper。发生这种情况是因为 theme_dict 在最后一轮中确实指向与 paper 相关的主题。


渴望

我想做的是能够以某种方式“硬编码”theme_dict 指向的内容,以便 themes.register 指向一个包含生成的字典的函数每次通过。但无论我如何尝试思考这个问题,我都无法让 lambda 函数创建一个“一成不变”的函数。相反,该函数将只是 return 最后一次迭代中的任何内容。

因此,虽然这是 一个 altair 特定问题,但我觉得解决方案应该是通用的:

如何创建一个函数,该函数将传递在创建函数时确定的值,而不是在调用函数时确定的值?


更完整的例子

根据评论中的要求,这是我的代码的更完整快照:

for theme_key, theme_file in THEME_FILES.items(): # THEME_FILES is a dictionary with values containing file paths
    with open(theme_file) as theme_fh:
        raw_theme = toml.load(theme_fh)
    dims = {
        "width": raw_theme.pop("width"),
        "height": raw_theme.pop("height"),
    }
    raw_theme["view"] = dims
    final_theme["config"] = raw_theme
    themes.register(theme_key, lambda: final_theme)

(为可怜的变量名道歉...我对我遇到的问题感到沮丧,重命名时认为这个问题是对全局变量的意外覆盖。)

再次说明一下,theme_key 已正确注册。例如,如果不同的键是 ['light', 'dark', 'paper'],那么我可以看到所有 3 个。但是所有三个都指向上一次迭代制作的主题。在这种情况下,'paper' 主题。

如果我只是重复其中的一个主题,它就可以完美地工作。所以我相当有信心我已经查明了问题所在。

您可以使用工厂函数来生成 returns 主题字典的函数。字典将在返回函数的闭包中被捕获。

def factory(x):
    def theme():
        return x
    return theme

for theme_name, theme_dict in mythemes.items():
    themes.register(theme_name, factory(theme_dict))

如果我对问题的理解正确,这应该会产生预期的效果:

themes.register(theme_name, lambda theme_dict=theme_dict: theme_dict)