【问题标题】:Is it possible to create a function that returns a static value generated upon creation, not upon calling the function?是否可以创建一个返回创建时生成的静态值的函数,而不是在调用函数时?
【发布时间】:2020-07-14 19:28:32
【问题描述】:

问题陈述:

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


背景

使用altair我正在尝试设置多个主题,为了创建一个主题,我需要使用类似这样的代码"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'注册启用,因此altair 的任何未来使用都将包括这些默认值。

注意函数black_marks返回一个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')),但它们都指向最后一个,@ 987654334@。这是因为theme_dict在最后一轮中确实指向了与paper相关的主题。


愿望

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

所以,虽然这是 某种 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' 主题。

如果我只迭代其中一个主题,它就可以完美运行。所以我相当有信心我已经查明了问题所在。

【问题讨论】:

  • 我正在努力了解您在这里要做的事情。您会在“问题”下的示例中添加代码以显示您在该示例下方段落中用文字解释的内容吗?具体来说,我需要查看mythemes 是如何创建/定义的。我认为这将有助于澄清您的要求。
  • @Code-Apprentice 完成。见最后的附录。
  • 通过下面的答案,我发现问题出在lambda 函数及其闭包上。有关其工作原理的更多详细信息,请参阅stackoverflow.com/questions/2295290/…。 James 的答案中的 factory() 函数提供了对 lambda 不做的参数的预期闭包。

标签: python return-value static-linking altair


【解决方案1】:

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

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

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

【讨论】:

  • 好的!一石两鸟!现在我终于理解了工厂函数的意义,我一直听说并阅读过它,但在你看到一个用例之前,什么都没有真正点击。 :) 非常感谢,@James。
  • 如果您指出此处的factory() 函数与OP 代码中的lambda 之间的区别,将对以后的读者有所帮助。当然,你不想重复所有已经可用的闭包信息,但至少提及这个术语将为那些对正确术语感兴趣的人提供谷歌搜索。
【解决方案2】:

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

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

【讨论】:

  • 不,这不起作用。不幸的是,我无法创建带有任何参数的 lambda 函数。作为altairs API 的一部分,它必须是无参数的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-05-13
  • 2012-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-14
相关资源
最近更新 更多