包上的 `del` 有某种记忆

Posted

技术标签:

【中文标题】包上的 `del` 有某种记忆【英文标题】:`del` on a package has some kind of memory 【发布时间】:2018-07-26 08:10:44 【问题描述】:

del 似乎有一些让我困惑的记忆。请参阅以下内容:

In [1]: import math

In [2]: math.cos(0)
Out[2]: 1.0

In [3]: del math.cos

In [4]: math.cos(0)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-4-9cdcc157d079> in <module>()
----> 1 math.cos(0)

AttributeError: module 'math' has no attribute 'cos'

很好。让我们看看如果我们删除整个数学包会发生什么:

In [5]: del math

In [6]: math.cos(0)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-6-9cdcc157d079> in <module>()
----> 1 math.cos(0)

NameError: name 'math' is not defined

所以现在数学本身已经消失了,正如预期的那样。

现在让我们再次导入数学:

In [7]: import math

In [8]: math.cos(0)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-8-9cdcc157d079> in <module>()
----> 1 math.cos(0)

AttributeError: module 'math' has no attribute 'cos'

所以不知何故,交互式 python 记得即使在我们删除了整个数学包并再次导入之后,math.cos 也被专门删除了。

python 将这些知识保存在哪里?我们可以访问它吗?我们可以改变它吗?

【问题讨论】:

我不知道他为什么需要它,但这是了解其工作原理的好方法。 1) 使用标准库并不意味着您可能对非标准库感兴趣。 2) 它让您了解库机制是如何工作的,这可能会影响到时间流逝、资源使用、硬盘访问、库中的静态变量等。 3) 它让你了解解释器是如何处理一些问题的,并将其转换为另一个类似的问题,甚至是你曾经的解释器,或者努力让它变得更好 4) 好奇心。它是打开知识之门的钥匙。正如我父亲所说:打破你的学习。你永远不知道你的好奇心会带你走多远。我父亲也说过:怀疑的时候就是学习的时候(把任何怀疑当作学习的灵感)。此外,Isaac Asimov 在预测互联网时所说的好奇心也很有趣。 【参考方案1】:

我会说这个包仍然被视为进口的。所以再次执行import math 只是重新声明名称,但内容旧。

您可以使用reload 来确保您的模块再次完整,除了某些版本的python 还需要删除sys.modules 中的条目,这使得reload 的使用变得多余:

import math
del math.cos
del math
sys.modules.pop("math")   # remove from loaded modules
import math
print(math.cos(0))  # 1.0

(各种python版本之间的差异,reloadimport在后续问题中讨论:Should importlib.reload restore a deleted attribute in Python 3.6?)

【讨论】:

恐怕 reload(math) 不会重新引入 math.cos 函数(至少在我的机器上)。我对此主题有一个单独的问题:***.com/questions/48808456/… 是的,我编辑显示一个无需重新加载即可工作的版本。【参考方案2】:

del math 根本不删除包,它只是删除了当前模块中的本地名称 math

与任何其他对象一样,如果对数学模块的任何其他引用存在于任何地方,则它会保存在内存中。

特别是,sys.modules 始终是所有已加载模块的字典,因此至少总有一个引用。

编辑:但是有一种方法可以实际重新加载模块,imp.reload

不幸的是,我无法让它在这种情况下工作,重新加载需要随机模块(可能是创建已编译 Python 文件的某些部分),随机模块需要math.cos,然后它就消失了。即使首先导入random 也没有错误,但math.cos 不会再次出现;我不知道为什么,可能是因为它是一个内置模块。

【讨论】:

【参考方案3】:

一个包只从磁盘读取一次,然后作为可变单例存储在内存中。第二次导入它时,您会得到与之前导入的完全相同的单例,但它仍然缺少它的cosdel math 只是删除了它的本地名称,它并没有从 Python 整体“取消导入”包。

【讨论】:

添加更多细节——第二个import math 将在sys.modules 中查找已加载(并且仍然是变异的)模块,并跳过任何从磁盘再次加载的尝试。您可以尝试将其从sys.modules 中删除以强制重新加载,或使用importlib 等。 正如我在下面和其他地方的评论中所说,重新加载在 python 3.6.4 中对我没有帮助。这是重新加载错误还是什么? ***.com/questions/48808456/… @Aguy 我不明白你的评论。如果您执行import math 然后del math.cos 然后reload(math),则math.cos 再次可用,因为reload 重新执行模块初始化并在sys.modules 中重新填充模块的字典。关于reload 的一个怪癖是它将保留extra 属性。所以如果你手动创建了math.foo,那么当你reload(math)时,math.foo会保留。 @ely reload 在这种情况下在我的 python 3.6.4 上不起作用。请参阅上面评论中的链接。 @Aguy 后续问题linked here 调查 Python 3.6 中奇怪的重新加载行为。

以上是关于包上的 `del` 有某种记忆的主要内容,如果未能解决你的问题,请参考以下文章

如何记忆 **kwargs?

结合记忆和尾递归

函数式编程语言中的自动记忆

直观理解LSTM(长短时记忆网络)

自用深度记忆网络在方面级情感分析上的应用

怎样记忆电脑键盘上的26个字母的顺序