Подтвердить что ты не робот

Выполнение кода 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". Что это значит?

4b9b3361

Ответ 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__' и проигнорируйте другие детали ненадежной реализации.