`del` на пакете имеет какую-то память

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'

Fine. Посмотрим, что произойдет, если мы удалим весь математический пакет:

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 был удален специально даже после того, как мы удалили весь математический пакет и снова импортировали его.

Где питон хранит эти знания? Можем ли мы получить к нему доступ? Можем ли мы изменить его?

Ответ 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 обсуждается в следующем вопросе: Должен importlib.reload восстановить удаленный атрибут в Python 3.6?)

Ответ 2

Пакет только считывается с диска один раз, а затем сохраняется в памяти как изменяемый singleton. Во второй раз, когда вы импортируете его, вы получаете тот же самый синглтон, который вы ранее импортировали, и он по-прежнему не имеет своего cos. del math просто удаляет локальное имя для него, он не "не импортирует" пакет из общего Python.

Ответ 3

del math не удаляет пакет вообще, он просто удаляет локальное имя math в текущем модуле.

Как и любой другой объект, если какие-либо другие ссылки на математический модуль существуют где угодно, он сохраняется в памяти.

И, в частности, sys.modules всегда является словарем всех загруженных модулей, поэтому по крайней мере там всегда есть ссылка.

Изменить: Но есть способ действительно перезагрузить модуль, imp.reload.

К сожалению, я не могу заставить его работать для этого случая, перезагрузка требует случайного модуля (возможно, для создания части компилируемого файла Python), для случайного модуля требуется math.cos, и он исчез. Даже при импорте random сначала нет ошибки, но math.cos не появляется снова; Я не знаю, почему, может быть, потому, что это встроенный модуль.