使用内置函数跨模块存储全局变量

2024-04-24 20:06:52 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个Jupyter笔记本,我想用它来运行python‘helper’文件中定义的一系列函数。不过,笔记本确实有一些用户可以更改的变量(所以它们就像常量一样,我想)。我希望这些变量也可以从helper文件访问。我不希望将这些变量传递给笔记本中的每个函数调用。在

我发现在笔记本中定义这些变量时,以下几点是有效的:

import builtins

builtins.my_variable = my_value

变量'my'u variable'现在可以在Jupyter笔记本和helper文件中使用。在

注意:以这种方式定义变量后,如果我在笔记本中键入help(builtins),并一直滚动到底部,那么在“DATA”部分下,我会找到列出的变量。在

另一个有效的方法是:

^{pr2}$

有人能解释一下这些东西为什么/是如何工作的吗?如果使用它们有什么问题,如果有,可能是更好的方法吗?在


Tags: 文件方法函数用户importhelper定义value
2条回答

首先:我建议将变量传递给helper模块函数,而不是依赖全局状态。如果您需要某种全局状态,并且不想一次又一次地传递它,请考虑将一些函数分组到类中,在这些类中,状态被传递给类的初始值设定项并存储在实例中。这样,调用实例的方法就隐式地传递实例,从而传递所需的状态,而重复性最小。我将在这个答案的底部提供一个简单的例子。在

{2}的查找结果将修改你的位置值。这可能会降低所有代码的速度,无论在何处(尤其是如果这意味着调整builtins模块的底层dict,可能使其不再适合缓存)。在

从面向未来的角度来看,已经有了occasional proposals to optimize lookups in ^{} based on its presumed static contents;虽然大多数建议处理builtins被修改的情况,但是所述优化的效果可能会丢失(恢复到只根据需要执行查找)。这也有先例;在cpython3.3之前,建议在__init__完成之前创建实例的所有属性,之后不应删除或向实例添加任何属性(给定属性的值仍然可以更改)。但是在3.2和更早的版本中,忽略这个建议并没有真正的惩罚。Starting in 3.3, classes that followed this advice got a massive reduction in per instance memory overhead;不遵循建议的类一无所获。在

修改builtins还有其他问题,例如:

  1. 可能导致builtins的底层dict增大,减少内存访问位置
  2. 可能会导致查找特定内置项的额外冲突(仅仅因为新属性的存在而减慢对有用内置项的访问)
  3. 可能会隐藏其他模块中的错误,例如,一个模块本应创建一个与您在本地插入builtins相同的名称的变量,但要么没有这样做,而是默默地使用您的定义,或者更糟的是,故意依赖不存在的名称来惰性地初始化它自己的属性,而现在却从未初始化它,用你的定义代替
  4. 使代码难以维护;如果我在模块中看到对名为foo的变量的引用,我希望能够在模块中找到定义,或者通过查看导入找到定义的源(from x import *语法妨碍了这一点,这就是为什么静态代码检查程序经常将from x import *报告为错误)。如果它是在一些不相关的模块中被秘密地创建并插入builtins(或者更糟的是,从许多不同的不相关的模块中变异而来),我会对那些犯下这种暴行的人感到愤怒。在

重点是,修改builtins是个坏主意。它可能会起作用,但不要这样做。在

您的helpers模块方法并不完全糟糕,但在实践中,我建议直接在helpers.py中定义共享值并将其视为常量,而不是让其他模块在那里创建它们(这会导致许多与修改builtins相同的问题,只是问题的范围更有限)。在

这些方法工作的原因是模块大多只是围绕字符串键控的Pythondicts的语法糖。你可以向Python中的大多数(虽然不是所有)对象添加新属性,模块本身就是对象(而不是该一般规则的例外之一)。在

helper.my_variable = my_value

可以归结为:

^{pr2}$

而且由于相同的helper被它的所有导入程序看到(一个模块被缓存在第一个import上,所有后续的{}都会返回对同一个缓存副本的引用),所以所有这些导入器都看到了修改。在


我在上面提到的更好的解决方案是改变:

^{3}$

打电话的人:

>>> 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()

或者只使用一个给定的set值:

>>> helpers.Helper(a=5).func1()

默认值的使用方式是:

^{8}$

这避免了多线程(或可重入代码)对helpers的全局状态进行互不兼容更改的问题(因为现在状态存储在实例中,这些实例是独立拥有和管理的)。使用带有默认参数的初始值设定项意味着您永远不会丢失原始的默认值;您可以随时生成新的副本。在

首先,我要说的是,我试图避免全球国家,因为有时说它是不可避免的。在Python中,创建全局状态的最简单方法是只在其中包含变量的模块。例如

# constants.py
constant1 = 'A Constant'
constant2 = 'Another Constant'
constant3 = 'Absolutely last constant'

在Jupyter笔记本中,您可以执行以下操作:

^{pr2}$

在你的功能模块中,你也可以这样做。在

基本上,你的第二种方法就是要走的路。在

警告Python实际上并没有常量的概念,因为您可以做类似constants.constant1 = 'Some new value'的操作,这会改变constants.constant1的新赋值的值。在

相关问题 更多 >