包上的 `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版本之间的差异,reload
和import
在后续问题中讨论: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】:一个包只从磁盘读取一次,然后作为可变单例存储在内存中。第二次导入它时,您会得到与之前导入的完全相同的单例,但它仍然缺少它的cos
。 del 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` 有某种记忆的主要内容,如果未能解决你的问题,请参考以下文章