【发布时间】: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