Выполнение кода Python с параметром -m или нет
У интерпретатора python есть опция модуля -m
, которая "Запускает модуль модуля библиотеки как script".
С помощью этого кода python a.py:
if __name__ == "__main__":
print __package__
print __name__
Я протестировал python -m a
, чтобы получить
"" <-- Empty String
__main__
тогда как python a.py
возвращает
None <-- None
__main__
Для меня эти два вызова кажутся одинаковыми, за исключением того, что __package__ не является None при вызове с параметром -m.
Интересно, что с python -m runpy a
я получаю то же самое, что и python -m a
с модулем python, скомпилированным для получения a.pyc.
Какая (практическая) разница между этими вызовами? Любые плюсы и минусы между ними?
Кроме того, David Beazley Python Essential Reference объясняет это как "Параметр -m запускает библиотечный модуль как script, который выполняется внутри модуля __main__ до выполнения основного script". Что это значит?
Ответы
Ответ 1
Когда вы используете флаг командной строки -m
, Python импортирует для вас модуль или пакет, а затем запускает его как script. Если вы не используете флаг -m
, файл, который вы назвали, запускается как только script.
Различие важно при попытке запустить пакет. Существует большая разница между:
python foo/bar/baz.py
и
python -m foo.bar.baz
как и в последнем случае, foo.bar
импортируется, а относительный импорт будет корректно работать с foo.bar
в качестве отправной точки.
Демо:
$ mkdir -p test/foo/bar
$ touch test/foo/__init__.py
$ touch test/foo/bar/__init__.py
$ cat << EOF > test/foo/bar/baz.py
> if __name__ == "__main__":
> print __package__
> print __name__
>
> EOF
$ PYTHONPATH=test python test/foo/bar/baz.py
None
__main__
$ PYTHONPATH=test bin/python -m foo.bar.baz
foo.bar
__main__
В результате Python действительно заботится о пакетах при использовании переключателя -m
. Обычный script никогда не может быть пакетом, поэтому __package__
устанавливается на None
.
Но запустите пакет или модуль внутри пакета с -m
, и теперь есть хотя бы возможность пакета, поэтому переменная __package__
установлена в строковое значение; в приведенной выше демонстрации установлено значение foo.bar
, для простых модулей, не входящих в пакет, устанавливается пустая строка.
Что касается модуля __main__
; Python импортирует скрипты, запущенные, так как это будет обычный модуль. Новый объект модуля создается для хранения глобального пространства имен, хранящегося в sys.modules['__main__']
. Это то, к чему относится переменная __name__
, это ключ в этой структуре.
Для пакетов вы можете создать модуль __main__.py
и выполнить этот запуск при запуске python -m package_name
; на самом деле, единственный способ запустить пакет как script:
$ PYTHONPATH=test python -m foo.bar
python: No module named foo.bar.__main__; 'foo.bar' is a package and cannot be directly executed
$ cp test/foo/bar/baz.py test/foo/bar/__main__.py
$ PYTHONPATH=test python -m foo.bar
foo.bar
__main__
Итак, при названии пакета для работы с -m
, Python ищет модуль __main__
, содержащийся в этом пакете, и выполняет это как script. Затем это имя по-прежнему устанавливается на __main__
, а объект модуля все еще сохраняется в sys.modules['__main__']
.
Ответ 2
Выполнение кода Python с опцией -m или нет
Используйте флаг -m
.
Результаты практически одинаковы, когда у вас есть скрипт, но когда вы разрабатываете пакет без флага -m
, нет способа заставить импорт работать правильно, если вы хотите запустить подпакет или модуль в пакете как главная точка входа в вашу программу (и поверьте мне, я пробовал.)
Документы
Как документы на флаге -m сказать:
Найдите в sys.path указанный модуль и выполните его содержимое как модуль __main__
.
и
Как и в случае опции -c, текущий каталог будет добавлен в начало sys.path.
так
python -m pdb
примерно эквивалентно
python /usr/lib/python3.5/pdb.py
(при условии, что в вашем текущем каталоге pdb.py нет пакета или скрипта)
Объяснение:
Поведение сделано "намеренно похожим" на сценарии.
Многие стандартные библиотечные модули содержат код, который вызывается при их выполнении в виде скрипта. Примером является модуль timeit:
Некоторый код на Python предназначен для запуска в виде модуля: (я думаю, что этот пример лучше, чем пример doc командной строки)
$ python -m timeit '"-".join(str(n) for n in range(100))'
10000 loops, best of 3: 40.3 usec per loop
$ python -m timeit '"-".join([str(n) for n in range(100)])'
10000 loops, best of 3: 33.4 usec per loop
$ python -m timeit '"-".join(map(str, range(100)))'
10000 loops, best of 3: 25.2 usec per loop
И из примечаний к выпуску для Python 2.4:
Опция командной строки -m - python -m modulename найдет модуль в стандартной библиотеке, и вызвать его. Например, python -m pdb
эквивалентно python /usr/lib/python2.4/pdb.py
Дополнительный вопрос
Кроме того, David Beazley Python Essential Reference объясняет это как Опция -m запускает модуль библиотеки в виде скрипта, который выполняется внутри модуля __main__
до выполнения основного скрипта ".
Это означает, что любой модуль, который вы можете найти с помощью оператора импорта, может быть запущен как точка входа в программу - если у него есть блок кода, обычно ближе к концу, с if __name__ == '__main__':
.
-m
без добавления текущего каталога в путь:
Комментарий здесь в другом месте говорит:
То, что опция -m также добавляет текущий каталог в sys.path, очевидно, является проблемой безопасности (см. атака предварительной загрузки). Это поведение похоже на порядок поиска библиотек в Windows (до того, как он был недавно усилен). Жаль, что Python не следует тренду и не предлагает простой способ отключить добавление. в sys.path
Ну, это демонстрирует возможную проблему - (в окнах удалите кавычки):
echo "import sys; print(sys.version)" > pdb.py
python -m pdb
3.5.2 |Anaconda 4.1.1 (64-bit)| (default, Jul 5 2016, 11:41:13) [MSC v.1900 64 bit (AMD64)]
Используйте флаг -I
, чтобы заблокировать его для производственных сред (новое в версии 3.4):
python -Im pdb
usage: pdb.py [-c command] ... pyfile [arg] ...
etc...
из документов:
-I
Запустите Python в изолированном режиме. Это также подразумевает -E и -s. В изолированном режиме sys.path не содержит ни каталога scripts, ни каталога site-packages пользователя. Все переменные окружения PYTHON * также игнорируются. Дополнительные ограничения могут быть наложены, чтобы запретить пользователю вводить вредоносный код.
Что делает __package__
?
Он разрешает явный относительный импорт, хотя он не особенно уместен для этого вопроса - посмотрите этот ответ здесь: Какова цель " __ package __ " атрибут в Python?
Ответ 3
Основной причиной запуска модуля (или пакета) в качестве script с -m является упрощение развертывания, особенно в Windows. Вы можете установить скрипты в том же месте в библиотеке Python, где обычно идут модули, вместо того, чтобы загрязнять PATH или глобальные исполняемые каталоги, такие как ~/.local(каталог скриптов для каждого пользователя смешно трудно найти в Windows).
Затем вы просто набираете -m, а Python автоматически находит script. Например, python -m pip
найдет правильный пип для того же экземпляра интерпретатора Python, который его выполняет. Без -m, если у пользователя установлено несколько версий Python, какой из них будет "глобальным" пиком?
Если пользователь предпочитает "классические" точки входа для сценариев командной строки, их можно легко добавить как небольшие скрипты где-нибудь в PATH, или pip может создать их во время установки с параметром entry_points в setup.py.
Так что просто проверьте __name__ == '__main__'
и проигнорируйте другие детали ненадежной реализации.