我有一个Jupyter笔记本,我想用它来运行一系列在python'helper'文件中定义的函数。然而,笔记本确实有一些用户可以更改的变量(因此它们就像我想的常量)。我希望这些变量也可以从帮助文件中访问。我宁愿不必将这些变量传递给笔记本中的每个函数调用。
我发现在笔记本中定义这些变量时,以下工作原理:
import builtins
builtins.my_variable = my_value
变量'my_variable'现在可以在Jupyter笔记本和帮助文件中使用。
注意:以这种方式定义变量后,如果我在笔记本中键入help(builtins)
并一直滚动到底部,在'DATA'部分下我会找到我的变量。
另一件事有效:
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
,可能使它不再适合缓存)。
从未来的角度来看,有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
还有其他问题,例如:
dict
的基础builtins
增加大小,减少内存访问位置builtins
本地推送的同名变量,但要么不能这样做并默默地使用你的定义,要么更糟糕的是,故意依赖不存在的名称懒惰地初始化它自己的属性,现在永远不会使用您的定义来初始化它foo
的变量的引用,我希望能够在模块中找到定义,或者通过查看导入来找到定义的来源(from x import *
语法阻碍了这个,这就是为什么静态代码跳棋经常报告from x import *
是一个错误)。如果它是秘密地在一些不相关的模块中创建并推入builtins
(或者更糟糕的是,从许多不同的无关模块中变异),我会对那些犯下这种暴行的人生气。重点是,修改builtins
是个坏主意。它可能会奏效,但不要这样做。
你的helpers
模块方法并不完全可怕,但在实践中我建议直接在helpers.py
中定义共享值as aqual.abdullah suggests并将它们视为常量,而不是让其他模块在那里创建它们(这会导致许多与修改相同的问题) builtins
,问题范围更为有限)。
这些方法起作用的原因是模块大多只是字符串键控Python dict
s的语法糖。您可以向Python中的大多数(尽管不是全部)对象添加新属性,模块本身就是对象(而不是该一般规则的例外之一)。
helper.my_variable = my_value
真的归结为:
helper.__dict__['my_variable'] = my_value
并且由于所有导入器都看到相同的helper
(一个模块缓存在第一个import
上,所有后续的import
s都返回对同一个缓存副本的引用),所有这些都看到了修改。
我在顶部提到的更好的解决方案是改变:
# 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
的全局状态进行互不兼容的更改的问题(因为现在状态存储在实例中,这些实例是独立拥有和管理的)。使用带有默认参数的初始化程序意味着您永远不会丢失原始默认值;你总能制作一份新的副本。