>>> import sys
>>> del sys.modules['sys']
>>> import sys
>>> sys.modules
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: module 'sys' has no attribute 'modules'
Почему повторно импортированный модуль sys
больше не имеет некоторых атрибутов?
Я использую Python 3.12.3, и это происходит в macOS, Linux и Windows. Это происходит как в REPL, так и в скрипте .py. Этого не происходит в Python 3.11.
@dragoncoder047 Для меня это невозможно было воспроизвести в модуле, но оно было внутри REPL. (у меня 3.12)
Это не нормальное поведение для всех модулей. В частности, с sys
должна быть какая-то скрытая работа. Я не знаю, почему
Вероятно, есть что-то особенное в том, как REPL управляет своим пространством имен, которое отличается от того, как работают обычные файловые пространства имен при запуске сценариев.
В Python есть несколько специальных модулей, например builtins
. sys
также может получить специальное лечение.
Я могу воспроизвести это в скрипте и на REPL 3.12.3. Я тоже пробовал 3.11-3.8.
Дело не в одном, похоже, что около половина всех атрибутов теряется таким образом.
Похоже, что некоторые атрибуты модуля sys
не определены стандартным способом (github.com/python/cpython/blob/… ). Например. атрибут «modules» добавляется только при инициализации интерпретатора ( github.com/python/cpython/blob/…)
Я не могу воспроизвести это в Windows ни с помощью командной строки, ни с помощью IDLE (3.11.0), ни с помощью командной строки WSL2 Ubuntu (3.8.10).
@MarkRansom Попробуйте 3.12
«Доктор, мне больно, когда я это делаю!» «Не делай этого» — есть ли код, который можно было бы написать в реальной среде, где это когда-либо произойдет?
@CharlesDuffy Это не имеет отношения к вопросу «почему это происходит».
@nosteponsnek, но это имеет отношение к вопросу: «Почему это происходит?» здесь вообще по теме. Вопросы должны носить практический характер.
См. также: Meta Stack Exchange , В чем смысл закрывающих вопросов «почему» в языковом дизайне? -- это не вопрос о языковом дизайне как таковой, но точно так же нельзя разумно ожидать, что на него будет ответ, который изменит подход к практике разработки программного обеспечения.
То, как инициализируется sys
, определенно является темой для [python-internals] («Как Python работает под капотом?»).
@CharlesDuffy Понимая режим сбоя, вы сможете лучше понять, когда/почему/как перезагрузка модуля может оказаться ненадежной. Я думаю, что это хорошие практические знания.
Совершенно очевидно, что вам не следует этого делать, поскольку это, естественно, может привести к поломке чего-либо. В реализации Python, которую вы пробовали, случаются поломки именно таким образом, но Python не обещает, что произойдет. Большая часть того, что я собираюсь сказать, — это детали реализации.
Модуль sys
нельзя инициализировать, как обычные встроенные модули, поскольку он отвечает за большую часть основных функций. Вместо этого при запуске интерпретатора Python создает модуль sys
со специальной функцией _PySys_Create. Эта функция отвечает (частично) за правильную настройку модуля sys
, включая атрибут sys.modules
:
if (PyDict_SetItemString(sysdict, "modules", modules) < 0) {
goto error;
}
Когда вы это сделаете del sys.modules['sys']
, система импорта потеряет модуль sys
. Когда вы пытаетесь импортировать его снова, Python пытается создать совершенно новый модуль sys
, и он делает это так, как если бы sys
был обычным встроенным модулем. Проходит процедуру инициализации обычных встроенных модулей. Эта процедура оставляет новый модуль sys
в противоречивом, неправильно инициализированном состоянии, поскольку sys
никогда не предназначался для инициализации таким способом.
Есть поддержка перезагрузки sys
, хотя я думаю, что команда разработчиков подумывает о том, чтобы отказаться от этой поддержки — варианты использования очень неясны, и единственный, который мне приходит в голову, устарел. Часть повторной инициализации заканчивается тем, что попадает в путь кода , предназначенный для перезагрузки sys
, который обновляет свой __dict__
из копии, созданной в начале исходной инициализации sys
, прямо перед установкой sys.modules:
interp->sysdict_copy = PyDict_Copy(sysdict);
if (interp->sysdict_copy == NULL) {
goto error;
}
if (PyDict_SetItemString(sysdict, "modules", modules) < 0) {
goto error;
}
Эта копия обрабатывается по-другому в более ранних версиях Python, отсюда и различия в поведении, связанные с версией.
Но _PySys_Create был и в версии 3.11. Что изменилось в 3.12?
@nosteponsnek: Ответ расширен.
Как, черт возьми, это вообще произошло? Я не могу это воспроизвести (python 3.9.16 и 3.11.0)