首页 > 解决方案 > 如何方便地传递可以修改的全局设置

问题描述

我经常发现自己编写的代码需要一组参数,进行一些计算,然后将结果返回给另一个函数,这也需要一些参数来进行一些其他操作,等等。我最终得到了很多必须传递参数的函数,例如f(x, y, N, epsilon)which then 调用g(y, N, epsilon)等等。一直以来,我都必须在每个函数调用中包含参数Nepsilon并且不能忘记它们,这非常乏味。

我想要的是防止这种无休止地传递参数,同时仍然能够在一个for循环中更改其中一些参数,例如

for epsilon in [1,2,3]:
    f(..., epsilon)

我通常有大约 10 个参数来跟踪(这些是物理问题)并且事先不知道我必须改变哪些参数以及我可以保持默认值。

我想到的选项是

  1. 创建一个全局settings = {'epsilon': 1, 'N': 100}对象,供每个函数使用。然而,我一直被告知将东西放在全局命名空间中是不好的。我也担心这不会很好地修改循环中的settings对象。for
  2. settings在每个函数中将对象作为参数传递。这意味着我可以在对象经过时对其进行跟踪,并使其与for循环配合得很好。但是,它仍然在传递,这对我来说似乎很愚蠢。

有没有我没有考虑过的第三个选项?我能找到的大多数解决方案都适用于您的设置仅在启动程序时设置一次,然后保持不变的情况。

标签: pythondesign-patterns

解决方案


我相信这主要是编码风格之间的偏好问题。我将对您发布的内容以及其他一些替代方案发表我的看法。

首先,创建一个全局设置变量本身并不坏。如果将全局设置视为可变状态而不是不可变状态,则会出现问题。当您想动态修改参数时,这是一个危险的选择。

其次,传递设置在函数式语言中很常见,虽然如果你不习惯它可能看起来很笨拙,但这并不愚蠢。以这种方式传递状态的优点是您可以隔离您传递的字典设置中的更改而不会破坏原始设置,坏事是由于共享引用,python 在不变性方面有点混乱,您最终可能会制作许多 deepcopy 到防止数据竞争,这是完全低效的。除非您的 dict 没有嵌套,否则我不会那样做。

settings = {'epsilon': 1, 'N': 100}

# Unsafe but OK for plain dict
for x in [1, 2, 3]:
    f(..., dict(zip(settings, ('epsilon', x))))

# Safe way. 
ephimeral = copy.deepcopy(settings)
for x in [1, 2, 3]:
    ephimeral['epsilon'] = x
    f(..., ephimeral)

现在,还有另一种混合其他两种的选择,可能是我要采用的那种。创建一个全局不可变设置变量并编写函数签名以接受可选的关键字参数。这样,您就拥有了两者的优势,能够避免连续变量传递以及能够在没有数据竞争的情况下即时修改值:

def f(..., **kwargs):
    epsilon = kwargs.get('epsilon', settings['epsilon'])
    ...

您还可以创建一个封装上述行为的函数,以将变量提取与函数定义分离。有很多可能性。

希望这可以帮助。


推荐阅读