[英]Using builtins to store global variables across modules
我有一个Jupyter笔记本,我想简单地使用它运行一堆在python'helper'文件中定义的函数。 但是笔记本确实有一些用户可以更改的变量(所以它们就像我想的常量一样)。 我也希望这些变量也可以从帮助文件中访问。 我希望不必将这些变量传递给笔记本中的每个函数调用。
我发现在笔记本中定义这些变量时,以下工作有效:
import builtins
builtins.my_variable = my_value
变量“ my_variable”现在可以在Jupyter笔记本和帮助文件中使用。
注意:以这种方式定义变量后,如果我在笔记本中键入help(builtins)
并一直滚动到底部,则在“数据”部分下找到列出的变量。
另一件事有效:
import helper
helper.my_variable = my_value
有人可以解释为什么/这些东西如何工作,如果使用它们有任何问题,如果可以的话,这可能是一种更好的方法吗?
首先,让我说,我试图避免出现全球状态,因为有时候人们说这是不可避免的。 在Python中,创建全局状态的最简单方法是只包含一个带有变量的模块。 例如,
# constants.py
constant1 = 'A Constant'
constant2 = 'Another Constant'
constant3 = 'Absolutely last constant'
在Jupyter笔记本中,您可以执行以下操作:
import constants
在具有功能的模块中,您可以执行相同的操作。
基本上,第二种方法是要走的路。
CAVEAT Python并没有真正的常量概念,因为您可以执行诸如constants.constant1 = 'Some new value'
,这将具有更改为constants.constant1
新分配的值的效果。
首先:我建议将变量传递给helper
模块函数,而不要依赖于全局状态。 如果您需要某种全局状态并且不想一遍又一遍地传递它,请考虑将一些函数分组到类中,然后将状态传递给类的初始值设定项并存储在实例中。 这样,调用实例的方法隐式传递实例,从而以最小的重复传递所需的状态。 我将在此答案的底部提供一个简单的示例。
修改builtins
的内容将意味着用您的价值膨胀对最后一个位置的查找。 可以想象,这会减慢所有地方的所有代码的速度(尤其是如果这意味着调整builtins
模块的底层dict
大小,可能使其不再适合缓存)。
从面向未来的角度来看, 偶尔有一些建议基于其假定的静态内容来优化builtins
的查找 ; 尽管大多数建议都处理修改builtins
的情况,但是这些优化的效果可能会丢失(恢复为仅按需执行查找)。 这也有先例。 在CPython 3.3之前,建议在__init__
完成之前创建实例的所有属性,并且之后不应该删除或将任何属性添加到实例(给定属性的值仍可以更改)。 但是在3.2和更早的版本中,忽略此建议并没有真正的惩罚。 从3.3开始,遵循该建议的类在每个实例上的内存开销大大减少了 ; 没有遵循建议的课程没有任何收获。
修改builtins
还有其他问题,例如:
dict
的builtins
扩大规模,降低内存访问局部性 builtins
模块中推入的名称相同,但这样做要么失败,并且静默地使用您的定义,要么更糟,故意依赖不存在的名称初始化自己的属性,现在不再使用定义来初始化它 foo
的变量的引用,则希望能够在模块中找到定义,或通过查看from x import *
来找到定义的源( from x import *
语法可以看出,这是为什么静态代码检查器经常from x import *
报告错误)。 如果它是在其他不相关的模块中秘密创建的,并且被builtins
(或者更糟的是,从许多不相关的模块中进行了变异)推到了我的builtins
,那么我将对犯下这种残酷行为的人感到愤怒。 重点是,修改builtins
函数不是一个好主意。 它可能会起作用,但不要这样做。
您的helpers
模块方法并不完全糟糕,尽管在实践中,我建议直接在aers.abdullah的建议下将helpers.py
的共享值定义为aqual.abdullah并将其视为常量,而不是在此处创建其他模块(这会导致很多与修改builtins
相同的问题,只是问题的范围更有限)。
这些方法行之有效的原因在于,模块大多只是围绕字符串键Python dict
的语法糖。 您可以在Python中的大多数(尽管不是全部)对象中添加新属性,而模块本身就是对象(而不是该通用规则的例外之一)。
helper.my_variable = my_value
真正归结为:
helper.__dict__['my_variable'] = my_value
并且由于其所有导入程序都可以看到相同的helper
程序(在第一次import
缓存了一个模块,而所有后续import
都返回了对同一缓存副本的引用),因此他们所有人都看到了修改。
我在顶部提到的更好的解决方案是更改:
# 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
的全局状态进行相互不兼容更改的问题(因为该状态存储在实例中,这些实例是独立拥有和管理的)。 使用带有默认参数的初始化程序意味着您永远不会丢失原始默认值。 您可以随时制作一份新的副本。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.