我有一个Jupyter笔记本,我想简单地使用它运行一堆在python'helper'文件中定义的函数。但是笔记本确实有一些用户可以更改的变量(所以它们就像我想的常量一样)。我也希望这些变量也可以从帮助文件中访问。我希望不必将这些变量传递给笔记本中的每个函数调用。
我发现在笔记本中定义这些变量时可以使用以下功能:
import builtins
builtins.my_variable = my_value
变量'my_variable'现在可以在Jupyter笔记本和帮助文件中使用。
注意:以这种方式定义变量后,如果我在笔记本中键入help(builtins)
并一直滚动到底部,则在“数据”部分下会找到列出的变量。
另一种有效的方法:
import helper
helper.my_variable = my_value
有人可以解释为什么这些东西/如何工作,如果使用它们有任何问题,如果有的话,这可能是一种更好的方法?
答案 0 :(得分:0)
首先,我要说的是,我试图避开全局状态,因为有时被说成是不可避免的。在Python中,创建全局状态的最简单方法是只包含一个带有变量的模块。例如,
# constants.py
constant1 = 'A Constant'
constant2 = 'Another Constant'
constant3 = 'Absolutely last constant'
在Jupyter笔记本中,您可以执行以下操作:
import constants
,并且在具有功能的模块中,您可以执行相同的操作。
基本上,您的第二种方法是方法。
CAVEAT Python实际上没有常量的概念,因为您可以执行类似constants.constant1 = 'Some new value'
的操作,这将改变{{1}的新赋值}。
答案 1 :(得分:0)
开始:我建议将变量传递给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
模块方法并不完全糟糕,尽管在实践中我建议直接在as aqual.abdullah suggests中helpers.py
定义共享值并将其视为常量,而不是使用常量。其他模块在此处创建它们(这会引起许多与修改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
的全局状态进行相互不兼容更改的问题(因为该状态存储在实例中,这些实例是独立拥有和管理的)。使用带有默认参数的初始化程序意味着您永远不会丢失原始默认值。您可以随时制作一份新副本。