Какова цель ключа -m?

174

Не могли бы вы объяснить мне, в чем разница между звонками

python -m mymod1 mymod2.py args

и

python mymod1.py mymod2.py args

Кажется в обоих случаях mymod1.pyназывается и sys.argvесть

['mymod1.py', 'mymod2.py', 'args']

Так для чего -mнужен переключатель?

Шарль Брюне
источник
Пожалуйста, исправьте меня, если я ошибаюсь, но, -mкажется, поиск mymod1в пути по умолчанию библиотеки. Пример: python -m SimpleHTTPServerработает, а python SimpleHTTPServerне с can't open file 'SimpleHTTPServer': [Errno 2] No such file or directory.
Basj
7
Я действительно нашел ответ здесь более ясным: stackoverflow.com/questions/46319694/…
Casebash

Ответы:

137

Первая строка Rationaleраздела PEP 338 гласит:

В Python 2.4 добавлен ключ командной строки -m, позволяющий размещать модули, используя пространство имен модулей Python для выполнения в качестве сценариев. Мотивирующими примерами были стандартные библиотечные модули, такие как pdb и profile, и реализация Python 2.4 отлично подходит для этой ограниченной цели.

Таким образом, вы можете указать любой модуль в пути поиска Python, а не только файлы в текущем каталоге. Вы правы, что python mymod1.py mymod2.py argsимеет точно такой же эффект. Первая строка Scope of this proposalраздела гласит:

В Python 2.4 модуль, расположенный с использованием -m, выполняется так же, как если бы его имя файла было указано в командной строке.

С помощью -mбольшего возможно, например, работать с модулями, которые являются частью пакета, и т. Д. Это то, что представляет собой остальная часть PEP 338. Прочитайте это для получения дополнительной информации.

AGF
источник
47
Мое любимое использование -mэто python -m SimpleHTTPServer. Очень удобно, когда мне нужно поделиться некоторыми файлами без использования USB-накопителя.
arifwn
21
@arifwn Запуск Python3 требует небольшого обновления, python -m http.serverи это все еще круто!
Кит Рёд
12
TL; DR: 1) Вы можете запустить python -m package.subpackage.moduleи использовать обычное разрешающее оборудование, вам не нужно указывать точный .pyфайл. 2) Можно выполнить относительный импорт из запущенного модуля без каких-либо обходных путей, потому что его пакет будет загружен по пути. 3) Абсолютный импорт будет основываться на вашем текущем каталоге, а не на каталоге, в котором находится .pyфайл ( ''находится во главе sys.path, а не в том /path/to/myслучае, если скрипт находится в /path/to/my/script.py).
глухой
Что этот ответ не дает понять, так это то, что он работает только с подмножеством исполняемых модулей, то есть с __main__.pyфайлом. Большинство не делает и сломается, например, python -m sys 'print(sys.version)'терпит неудачу python: No code object available for sys. Предлагаю вам сделать это ясно в ответе.
SMCI
19

Стоит отметить, что это работает, только если в пакете есть файл.__main__.py В противном случае этот пакет не может быть выполнен напрямую.

python -m some_package some_arguments

Интерпретатор python будет искать __main__.pyфайл в пути пакета для выполнения. Это эквивалентно:

python path_to_package/__main__.py somearguments

Он выполнит содержимое после:

if __name__ == "__main__":
Marquez.Z
источник
2
Как насчет файла инициализации пакета? При наличии основного файла будет также вызываться init?
переменная
@variable Да init .py будет вызван до того, как будет вызван основной .py
Марк Рукер
1

Несмотря на то, что на этот вопрос задавали и отвечали несколько раз (например, здесь , здесь , здесь и здесь ), по моему мнению, ни один из существующих ответов полностью или кратко не отражает все значения -mфлага. Поэтому следующее попытается улучшить то, что было раньше.

Введение (TLDR)

Команда -mделает много вещей, не все из них обязательно будут нужны постоянно. Вкратце: (1) позволяет выполнять сценарии Python с помощью modulename, а не имени файла (2) позволяет выбрать каталог для добавления sys.pathдля importразрешения и (3) позволяет выполнять сценарии Python с относительным импортом из командной строки. ,

прелиминарии

Чтобы объяснить -mфлаг, мы сначала должны прояснить немного терминологии.

Во-первых, основная организационная единица Python называется модулем . Модуль поставляется в одном из двух вариантов: программные модули и модули пакетов. Модуль кода - это любой файл, который содержит исполняемый код Python. Модуль пакета - это каталог, который содержит другие модули (модули кода или модули пакета). Наиболее распространенным типом модулей кода являются *.pyфайлы, а наиболее распространенным типом модулей пакета являются каталоги, содержащие __init__.pyфайл.

Во-вторых, все модули могут быть однозначно идентифицированы двумя различными способами: <modulename>и <filename>. Модули чаще всего идентифицируются по имени модуля в коде Python (например, import <modulename>) и по имени файла в командной строке (например, python <filename>). Все интерпретаторы Python могут преобразовывать имена модулей в имена файлов с помощью набора четко определенных правил. Эти правила зависят от sys.pathпеременной, и, следовательно, отображение можно изменить, изменив это значение (подробнее о том, как это делается, см. PEP 302 ).

В-третьих, все модули (как код, так и пакет) могут быть выполнены (что означает, что код, связанный с модулем, будет оцениваться интерпретатором Python). В зависимости от метода выполнения и типа модуля, какой код оценивается и когда, может немного измениться. Например, если кто-то выполняет модуль пакета через, python <filename>тогда <filename>/__init__.pyбудет произведена оценка с последующим <filename>/__main__.py. С другой стороны, если один из них выполняет тот же модуль пакета, import <modulename>то __init__.pyбудут выполняться только пакеты .

Историческое развитие -m

Флаг -m впервые появился в Python 2.4.1 . Первоначально его единственная цель состояла в том, чтобы предоставить альтернативные средства идентификации модуля Python для выполнения. То есть, если бы мы знали <filename>и <modulename>модуль, и следующие две команды были эквивалентны: python <filename> <args>и python -m <modulename> <args>. Кроме того, согласно PEP 338 эта итерация -mработала только с модулями верхнего уровня (то есть с модулями, которые можно было найти непосредственно в sys.path без каких-либо промежуточных пакетов).

С завершением PEP 338-m функциональность была расширена для поддержки <modulename>представлений за пределами верхних modulenames уровня. Это означало, что имена http.serverбыли полностью поддержаны. Это усовершенствование также означало, что все пакеты в модуле теперь были загружены (то есть все __init__.pyфайлы пакетов были оценены) вместе с самим модулем.

Финальное усовершенствование основных функций -mпоявилось в PEP 366 . Благодаря этому обновлению -mпоявилась возможность поддерживать не только абсолютный импорт, но и явный относительный импорт. Это было достигнуто путем изменения __package__переменной для указанного модуля в -mкоманде.

Случаи использования

Есть два известных варианта использования флага -m:

  1. Выполнять модули из командной строки, для которых может не быть известно их имя файла. Этот вариант использования использует тот факт, что интерпретатор Python знает, как преобразовать имена модулей в имена файлов. Это особенно полезно, когда требуется запустить модули stdlib или сторонний модуль из командной строки. Например, очень немногие люди знают имя файла для http.serverмодуля, но большинство людей знают его имя по модулю, поэтому мы можем выполнить его из командной строки, используя python -m http.server.

  2. Для выполнения локального пакета, содержащего абсолютный импорт, без необходимости его установки. Этот вариант использования подробно описан в PEP 338 и использует тот факт, что текущий рабочий каталог добавляется, sys.pathа не каталог модуля. Этот вариант использования очень похож на использование pip install -e .для установки пакета в режиме разработки / редактирования.

Упущения

Со всеми улучшениями, внесенными -mза эти годы, у него все еще есть один существенный недостаток - он может выполнять только модули кода, написанные на python (то есть * .py). Например, если -mиспользуется для выполнения модуля скомпилированного кода на C, будет выдана следующая ошибка No code object available for <modulename>(подробнее см. Здесь ).

Подробные сравнения

Эффекты выполнения модуля с помощью команды python (т.е. python <filename>):

  • sys.path изменен, чтобы включить в <filename>
  • __name__ установлен на '__main__'
  • __package__ установлен на None
  • __init__.py не оценивается ни для какого пакета (включая его собственный для модулей пакета)
  • __main__.pyоценивается для пакетов модулей; код оценивается для модулей кода.

Эффекты выполнения модуля через оператор импорта (т. Е. import <modulename>):

  • sys.pathэто не изменены каким - либо образом
  • __name__ устанавливается в абсолютной форме <modulename>
  • __package__ устанавливается в непосредственный родительский пакет в <modulename>
  • __init__.py оценивается для всех пакетов (включая свой собственный для модулей пакета)
  • __main__.pyэто не оценивается для модулей пакетов; код оценивается для модулей кода

Эффекты выполнения модуля через флаг -m (т. Е. python -m <modulename>):

  • sys.path изменен, чтобы включить текущий каталог
  • __name__ установлен на '__main__'
  • __package__ устанавливается в непосредственный родительский пакет в <modulename>
  • __init__.py оценивается для всех пакетов (включая свой собственный для модулей пакета)
  • __main__.pyоценивается для пакетов модулей; код оценивается для модулей кода

Вывод

-mФлаг, в самом простом, средство для выполнения питона сценариев из командной строки с помощью modulenames , а не имена файлов. Кроме того, -mпредоставляет дополнительные функциональные возможности, сочетающие мощь importоператоров (например, поддержку явного относительного импорта и автоматическую __init__оценку пакетов ) с удобством командной строки python.

Марк Рукер
источник
Не могли бы вы также добавить использование вызова пакета, используя python -m packagenameкак указано здесь: stackoverflow.com/a/53772635/1779091
переменная
@variable хорошая идея, я добавил раздел «Вариант использования», который включает это.
Марк Ракер