`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'

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

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

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


person Aguy    schedule 15.02.2018    source источник
comment
Я не знаю, зачем ему это нужно, но это хороший способ понять, как это работает.   -  person ESL    schedule 15.02.2018
comment
@LightnessRacesinOrbit Потому что ранее они спрашивали как вернуть функцию math.cos после ее удаления.   -  person Graipher    schedule 16.02.2018
comment
@Graipher: мой вопрос остается в силе. Почему они вообще что-то удаляют из стандартной библиотеки?   -  person Lightness Races in Orbit    schedule 16.02.2018
comment
1) То, что вы делаете это со стандартной библиотекой, не означает, что вас могут заинтересовать нестандартные библиотеки.   -  person ESL    schedule 16.02.2018
comment
2) Это позволяет вам понять, как работает механизм библиотеки, что может иметь последствия, скажем, в отношении прошедшего времени, использования ресурсов, доступа к жесткому диску, статических переменных в библиотеке и т. д.   -  person ESL    schedule 16.02.2018
comment
3) Это заставляет вас узнать, как интерпретатор справляется с некоторыми проблемами, и перенести это на другие похожие проблемы, или даже сделать свой прежний интерпретатор, или работать над ним, чтобы сделать его лучше.   -  person ESL    schedule 16.02.2018
comment
4) Любопытство. Это ключ, который открывает двери к знаниям. Как говорит мой отец: ломая учишься. Никогда не знаешь, как далеко может завести тебя твое любопытство. Мой отец также говорит: время сомнений — это время учиться (воспринимайте любое сомнение как вдохновение, чтобы чему-то научиться). Также интересно, что Айзек Азимов сказал о любопытстве, когда предсказывал Интернет.   -  person ESL    schedule 16.02.2018


Ответы (3)


Я бы сказал, что пакет все еще рассматривается как импортированный. Таким образом, повторное выполнение 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?)

person Jean-François Fabre    schedule 15.02.2018
comment
Я боюсь, что reload(math) не повторно вводит функцию math.cos (по крайней мере, на моей машине). У меня есть отдельный вопрос по этой теме: stackoverflow.com/questions/48808456/ - person Aguy; 15.02.2018
comment
да, я отредактировал, чтобы показать версию, которая работает без перезагрузки. - person Jean-François Fabre; 15.02.2018

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

person deceze♦    schedule 15.02.2018
comment
Чтобы добавить еще одну деталь - второй import math будет искать в sys.modules загруженный (и все еще видоизмененный) модуль и пропустит любую попытку повторной загрузки с диска. Вы можете попробовать удалить его из sys.modules, чтобы принудительно перезагрузить, или использовать importlib и т. д. - person ely; 15.02.2018
comment
Как я сказал в комментарии ниже и в других местах, перезагрузка не помогла мне в python 3.6.4. Это баг перезагрузки или что? stackoverflow.com/questions/48808456/ - person Aguy; 15.02.2018
comment
@ Агай, я не понимаю твоего комментария. Если вы выполните import math, затем del math.cos, затем reload(math), то math.cos снова будет доступно, так как reload повторно выполняет инициализацию модуля и повторно заполняет словарь модуля в sys.modules. Одна особенность reload заключается в том, что он сохраняет дополнительные атрибуты. Так что если вы вручную создали math.foo, то при создании reload(math) останется math.foo. - person ely; 15.02.2018
comment
@ely reload не работает в этом случае на моем питоне 3.6.4. Смотрите ссылку в комментарии выше. - person Aguy; 15.02.2018
comment
@Aguy Дополнительный вопрос ссылка здесь, чтобы изучить странное поведение перезагрузки в Python 3.6. - person ely; 15.02.2018

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

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

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

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

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

person RemcoGerlich    schedule 15.02.2018