开始:我建议将变量传递给helper 模块函数,而不是依赖全局状态。如果您需要某种全局状态并且不想一遍又一遍地传递它,请考虑将一些函数分组到类中,将状态传递给类的初始化程序并存储在实例中。这样,调用实例的方法会隐式传递实例,从而传递所需的状态,并且重复最少。我将在此答案的底部提供一个简单的示例。
修改builtins 的内容将意味着用你的值膨胀最后的位置查找。可以想象,这可能会降低所有代码的速度(尤其是如果这意味着调整 builtins 模块的底层 dict 的大小,可能使其不再适合缓存)。
从面向未来的角度来看,有occasional proposals to optimize lookups in builtins based on its presumed static contents;虽然大多数提案处理 builtins 被修改的情况,但所述优化的效果可能会丢失(恢复为仅按需执行查找)。这也有先例;在 CPython 3.3 之前,建议在 __init__ 完成之前创建实例的所有属性,之后不应删除或添加任何属性(给定属性的值仍然可以更改)。但是在 3.2 和更早的版本中,忽略这个建议并没有真正的惩罚。 Starting in 3.3, classes that followed this advice got a massive reduction in per instance memory overhead;没有遵循建议的课程一无所获。
修改builtins还有其他问题,例如:
- 可能导致
builtins 的底层dict 大小增加,减少内存访问局部性
- 可能会在查找特定内置函数时产生额外的冲突(因为新属性存在而减慢对有用内置函数的访问速度)
- 可能隐藏其他模块中的错误,例如一个模块应该创建一个与您在本地推入
builtins 的名称相同的变量,但要么没有这样做,而是默默地使用您的定义,或者更糟糕的是,故意依赖不存在的名称来懒惰地初始化它自己的属性,并且现在永远不会初始化它,而是使用您的定义
- 使您的代码难以维护;如果我在模块中看到对名为
foo 的变量的引用,我希望能够在模块中找到定义,或者通过查看导入找到定义的来源(from x import * 语法阻碍了这一点,这这就是为什么静态代码检查器经常将from x import * 报告为错误的原因)。如果它是在其他不相关的模块中秘密创建并被推到builtins 中(或者更糟糕的是,从许多不同的不相关模块中变异),我会对犯下这种暴行的人感到愤怒。
重点是,修改builtins 是个坏主意。它可能会起作用,但不要这样做。
您的helpers 模块方法并不完全糟糕,但实际上我建议直接在as aqual.abdullah suggests 中定义helpers.py 中的共享值并将它们视为常量,而不是让其他模块在那里创建它们(这会导致许多与修改 builtins 相同的问题,只是问题范围更有限)。
这些方法起作用的原因是模块大多只是字符串键控 Python dicts 周围的语法糖。您可以为 Python 中的大多数(尽管不是全部)对象添加新属性,并且模块本身就是对象(而不是该一般规则的例外之一)。
helper.my_variable = my_value
真的可以归结为:
helper.__dict__['my_variable'] = my_value
并且由于所有导入器都可以看到相同的helper(一个模块缓存在第一个import,并且所有后续imports 都返回对相同缓存副本的引用),所有这些都看到修改。
我在顶部提到的更好的解决方案是改变:
# helpers.py
a = 1
b = 2
def func1():
return a + b
def func2():
return a * b
def func3():
return a / b
调用者在做:
>>> import helper
>>> helper.a = 5
>>> helper.func1()
到基于类的设计:
# helpers.py
class Helper:
def __init__(self, a=1, b=2):
self.a = 1
self.b = 2
def func1(self):
return self.a + self.b
def func2(self):
return self.a * self.b
def func3(self):
return self.a / self.b
用法在哪里:
>>> import helpers
>>> h = helpers.Helper(a=5)
>>> h.func1()
或者对于给定值集的单次使用:
>>> helpers.Helper(a=5).func1()
并且使用默认值只是:
>>> helpers.Helper().func1()
这避免了多个线程(或可重入代码)对helpers 的全局状态进行相互不兼容的更改的问题(因为现在状态存储在独立拥有和管理的实例中)。使用带有默认参数的初始化程序意味着您永远不会丢失原始默认值;您可以随时制作新副本。