Виконання коду Python з опцією -m чи ні


111

У інтерпретатора python є опція -m модуля, що "Запускає модуль модуля бібліотеки як сценарій".

З цим кодом 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.

Яка (практична) різниця між цими викликами? Якісь плюси і мінуси між ними?

Крім того, Python Essential Reference Девіда Бізлі пояснює це як " Параметр -m запускає модуль бібліотеки як сценарій, який виконується всередині модуля __main__ перед виконанням основного сценарію ". Що це означає?

Відповіді:


169

Коли ви використовуєте -mпрапор командного рядка , Python імпортує модуль або пакет для вас, а потім запустить його як сценарій. Якщо ви не використовуєте -mпрапор, названий вами файл запускається лише як сценарій .

Відмінність важлива при спробі запустити пакет. Існує велика різниця між:

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 python -m foo.bar.baz 
foo.bar
__main__

Як результат, Python повинен насправді піклуватися про пакети при використанні -mкомутатора. Звичайний сценарій ніколи не може бути пакетом, тому __package__встановлено значення None.

Але запустіть пакет або модуль всередині пакета з, -mі тепер існує, принаймні, можливість пакету, тому __package__змінна встановлена ​​на рядок; у наведеній вище демонстрації встановлено foo.bar, що для простих модулів, які не знаходяться всередині пакета, він встановлюється в порожню рядок.

Що стосується __main__ модуля ; Python імпортує сценарії, які виконуються як звичайний модуль. Створено новий об'єкт модуля, який містить глобальну область імен, що зберігається в sys.modules['__main__']. Це те, на що __name__посилається змінна, вона є ключовою в цій структурі.

Для пакетів ви можете створити __main__.pyмодуль і виконати його під час запуску python -m package_name; в тому , що це єдиний спосіб , яким Ви можете запустити пакет як сценарій:

$ 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__модуль, що міститься в цьому пакеті, і виконує його як сценарій. Потім його ім'я все ще встановлюється __main__, а об'єкт модуля все ще зберігається в sys.modules['__main__'].


1
Що насправді означає команда PYTHONPATH=test python -m foo.bar? Чи можете ви пояснити це детально?
Андрій

3
@Andriy: PYTHONPATHвстановлює змінну середовища; він розширює серію каталогів, де Python шукатиме модулі при імпорті; тут він додає testкаталог до цієї серії. Поклавши його в один і той же командний рядок, він застосовується лише до тієї єдиної pythonкоманди. -mповідомляє Python імпортувати певний модуль, як ніби ви бігли import foo.bar. Однак Python автоматично запустить __main__модуль всередині пакета як сценарій при використанні цього комутатора.
Martijn Pieters

1
having to use -m always is not that user-.friendly.Я думаю, що суміш із використанням та не використанням -mє менш зручною для користувачів.
Сімін Джі

1
@SiminJie: скрипти можна відкрити будь-яким довільним шляхом, а потім їх батьківський каталог додається до шляху пошуку модуля. -mпрацює лише для поточного каталогу або каталогів, які вже зареєстровані на шляху пошуку. Це був мій пункт. -mце не те, що ви даєте кінцевим споживачам із цієї проблеми юзабіліті.
Martijn Pieters

1
@ flow2k: Я маю на увазі, що from Photos import ...скаржиться. Так би import Photos.<something>. import Photosпрацює лише тому, що Python підтримує пакети, розміщені з іменами (де два окремі дистрибутиви надаються Photos.fooта Photos.barокремо, і ними можна самостійно керувати).
Martijn Pieters

25

Виконання коду 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 знайде модуль у стандартній бібліотеці та викликає його. Наприклад, python -m pdb еквівалентноpython /usr/lib/python2.4/pdb.py

Подальше запитання

Крім того , Python Essential Reference Девід Бізлі пояснює його як «Опція -m запускає бібліотеку модуль як скрипт , який виконує всередині __main__модуля до виконання основного сценарію».

Це означає, що будь-який модуль, який ви можете шукати за допомогою імпорту, може бути запущений як пункт входу програми - якщо він містить блок коду, як правило, наприкінці if __name__ == '__main__':.

-m без додавання поточного каталогу до шляху:

Коментар тут у іншому місці говорить:

Опція -m також додає поточний каталог в sys.path, очевидно, є проблемою безпеки (див.: Атака перед завантаженням). Така поведінка схожа на порядок пошуку бібліотеки в Windows (раніше, ніж вона була загартована). Шкода, що Python не дотримується тенденції та не пропонує простий спосіб відключити додавання. до sys.path

Ну, це демонструє можливу проблему - (у Windows видаліть лапки):

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 не містить ні каталогу сценарію, ні каталогів сайту-пакунків користувача. Усі змінні середовища PYTHON * також ігноруються. Подальші обмеження можуть бути встановлені, щоб користувач не вводив шкідливий код.

Що робить __package__?

Це дає можливість явного відносного імпорту, що не особливо сприймає це питання, - див. Цю відповідь тут: Яка мета атрибута "__package__" в Python?


Який шлях додається до sys.path, коли використовується перемикач -m?
змінна

Я вже цитував це: "Як і у випадку -c, поточний каталог буде доданий до початку sys.path." але я уточнив, до чого відноситься цитата.
Aaron Hall

Я маю на увазі, що - припустимо, у D: \ тестовому каталозі я запускаю команду - python -m foo.bar.boo, тоді це додасть папку встановлення python або каталог D: \ test до sys.path? Я розумію, що він додасть d: \ test до sys.path, імпортує foo.bar та запустить boo script
змінна

@variable - так, спробуйте.
Аарон Холл

1

Основна причина запустити модуль (або пакет) як скрипт із -m - це спростити розгортання, особливо в Windows. Ви можете встановити скрипти в тому ж місці бібліотеки Python, куди зазвичай йдуть модулі - замість забруднення PATH або глобальних виконуваних каталогів, таких як ~ / .local (каталог скриптів на кожного користувача смішно важко знайти в Windows).

Потім ви просто наберете -m, і Python автоматично знайде сценарій. Наприклад, python -m pipзнайде правильний піп для того самого екземпляра інтерпретатора Python, який його виконує. Без -m, якщо у користувача встановлено кілька версій Python, який із них був би "глобальним" піппом?

Якщо користувач віддає перевагу «класичним» точкам вводу для скриптів командного рядка, їх можна легко додати як невеликі сценарії десь у PATH, або pip може створити їх під час встановлення за допомогою параметра entry_points у setup.py.

Тому просто перевіряйте __name__ == '__main__'та ігноруйте інші недостовірні деталі реалізації.


Опція -m також додає поточний каталог в sys.path, очевидно, є проблемою безпеки (див.: Атака перед завантаженням ). Така поведінка схожа на порядок пошуку бібліотеки в Windows (раніше, ніж вона була загартована). Шкода, що Python не дотримується тенденції та не пропонує простий спосіб відключити додавання. до sys.path.
ddbug
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.