Wykonywanie kodu Pythona z opcją-m lub nie

Interpreter Pythona ma -m Moduł opcja " uruchamia jako skrypt moduł biblioteki Moduł".

Z tym kodem Pythona a.py:

if __name__ == "__main__":
    print __package__
    print __name__

Przetestowałem python -m a Aby uzyskać

"" <-- Empty String
__main__

Natomiast python a.py zwraca

None <-- None
__main__

Dla mnie te dwa wywołania wydają się być takie same, z wyjątkiem __pakiet__ nie jest żaden, gdy wywołane jest opcją-m.

Co ciekawe, z python -m runpy a dostaję to samo co python -m a z modułem Pythona skompilowanym w celu uzyskania.pyc.

Jaka jest (praktyczna) różnica między tymi inwokacjami? Jakieś plusy i minusy między nimi?

David Beazley w Python Essential Reference wyjaśnia to jako " opcja-m uruchamia moduł biblioteki jako skrypt, który wykonuje wewnątrz modułu _ _ main _ _ przed wykonaniem skryptu". Co to znaczy?

Author: Rabarberski, 2014-03-07

3 answers

Kiedy używasz -m znacznik linii poleceń, Python zaimportuje dla Ciebie Moduł lub pakiet, a następnie uruchamia go jako skrypt. Jeśli nie używasz znacznika -m, nazwany plik jest uruchamiany jako po prostu skrypt.

Rozróżnienie jest ważne przy próbie uruchomienia pakietu. Istnieje duża różnica między:

python foo/bar/baz.py

I

python -m foo.bar.baz

Tak jak w tym drugim przypadku, foo.bar jest importowany i import względny będzie działał poprawnie z foo.bar jako początkiem punkt.

Demo:

$ 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__

W rezultacie Python musi dbać o Pakiety podczas używania przełącznika -m. Normalny skrypt nigdy nie może być pakietem, więc __package__ jest ustawione na None.

Ale uruchom pakiet lub Moduł wewnątrz pakietu z -mi teraz istnieje co najmniej możliwość pakietu, więc zmienna __package__ jest ustawiona na wartość łańcuchową; w powyższej demonstracji jest ustawiona na 'foo.bar', dla zwykłych modułów Nie wewnątrz pakietu jest ustawia pusty ciąg znaków.

Co do __main__ module , Python importuje Skrypty uruchamiane tak, jak importowałby zwykłe Moduły. Nowy obiekt modułu jest tworzony do przechowywania globalnej przestrzeni nazw i jest przechowywany w sys.modules['__main__']. Do tego odnosi się zmienna __name__, jest kluczem w tej strukturze.

Dla pakietów, możesz utworzyć __main__.py Moduł wewnątrz i uruchomić go podczas uruchamiania python -m package_name; w rzeczywistości jest to jedyny sposób, w jaki możesz uruchomić pakiet jako scenariusz:

$ 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__

Tak więc, nazywając pakiet do pracy z -m, Python szuka modułu __main__ zawartego w tym pakiecie i wykonuje go jako skrypt. Jego nazwa jest następnie nadal ustawiona na '__main__', a obiekt modułu jest nadal przechowywany w sys.modules['__main__'].

 191
Author: Martijn Pieters,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-09-14 09:16:44

Wykonywanie kodu Pythona z opcją-m lub nie

Użyj flagi -m.

Wyniki są prawie takie same, gdy masz skrypt, ale kiedy tworzysz pakiet, bez flagi -m, nie ma sposobu, aby import działał poprawnie, jeśli chcesz uruchomić podpakiet lub moduł w pakiecie jako główny punkt wejścia do twojego programu (i uwierz mi, próbowałem.)

Docs

Jak dokumenty na fladze-m mówią:

Szukaj sys.ścieżka do nazwanego modułu i wykonaj jego zawartość jako moduł __main__.

I

Tak jak w przypadku opcji-c, bieżący katalog zostanie dodany do początku sys./ align = "left" /

Więc

python -m pdb

Jest mniej więcej równoważne

python /usr/lib/python3.5/pdb.py

(zakładając, że nie masz pakietu lub skryptu w bieżącym katalogu o nazwie pdb.py)

Wyjaśnienie:

Zachowanie jest " celowo podobne do" Skrypty.

Wiele modułów biblioteki standardowej zawiera kod, który jest wywoływany podczas ich wykonywania jako skrypt. Przykładem może być moduł timeit :

Niektóre kody Pythona mają być uruchamiane jako moduł: (myślę, że ten przykład jest lepszy niż opcja wiersza poleceń przykład 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

A z informacji o wydaniu dla Pythona 2.4:

Opcja - m linii poleceń-python-m modulename znajdzie moduł w bibliotece standardowej i wywołaj ją. Na przykład, python -m pdb jest równoważne python /usr/lib/python2.4/pdb.py

Pytanie uzupełniające

Python Essential Reference Davida Beazleya wyjaśnia to jako " The -m opcja uruchamia moduł biblioteki jako skrypt, który wykonuje wewnątrz modułu __main__ przed wykonaniem skryptu głównego".

Oznacza to, że każdy moduł z instrukcją import może być uruchomiony jako punkt wejścia programu-jeśli posiada kod blok, zwykle pod koniec, z if __name__ == '__main__':.

-m bez dodawania bieżącego katalogu do ścieżki:

Komentarz Tu gdzie indziej mówi:

Że opcja-m dodaje również bieżący katalog do sys.path, jest oczywiście problemem bezpieczeństwa (patrz: atak preload). To zachowanie jest podobne do porządku wyszukiwania bibliotek w systemie Windows(zanim zostało ostatnio utwardzone). Szkoda, że Python nie podąża za trendem i nie oferuje prostego sposobu wyłączenia dodawania . do sys.ścieżka

Dobrze, to pokazuje możliwy problem - (w systemie Windows usuń cudzysłowy):

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)]

Użyj znacznika -I, aby zablokować to dla środowisk produkcyjnych (nowość w wersji 3.4):

python -Im pdb
usage: pdb.py [-c command] ... pyfile [arg] ...
etc...

From the docs :

-I

Uruchom Pythona w trybie izolowanym. Oznacza to również-E I-S. w trybie izolowanym sys.ścieżka nie zawiera katalogu skryptu ani katalogu site-packages użytkownika. All PYTHON* zmienne środowiskowe również są ignorowane. Mogą zostać nałożone dalsze ograniczenia, aby uniemożliwić użytkownikowi wstrzyknięcie złośliwego kodu.

Co robi __package__?

Umożliwia jawne importowanie względne, nie szczególnie germańskie dla tego pytania - zobacz tę odpowiedź tutaj: jaki jest cel atrybutu "__package__ " w Pythonie?

 30
Author: Aaron Hall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-10-19 12:33:03

Głównym powodem uruchomienia modułu (lub pakietu) jako skryptu z opcją-m jest uproszczenie wdrażania, szczególnie w systemie Windows. Skrypty można instalować w tym samym miejscu w bibliotece Pythona, gdzie normalnie idą Moduły - zamiast zanieczyszczać ścieżki lub globalne katalogi wykonywalne, takie jak~/.local (katalog skryptów per-user jest śmiesznie trudny do znalezienia w Windows).

Potem wystarczy wpisać -m i Python znajdzie skrypt automagicznie. Na przykład python -m pip znajdzie poprawny pip dla tego samego instancja interpretera Pythona, który go wykonuje. Bez -m, jeśli użytkownik ma zainstalowanych kilka wersji Pythona, która z nich byłaby "globalnym" pip?

Jeśli użytkownik preferuje "klasyczne" punkty wejścia dla skryptów wiersza poleceń, można je łatwo dodać jako małe Skrypty gdzieś w PATH, lub pip może utworzyć je w czasie instalacji z parametrem entry_points w setup.py.

Więc po prostu sprawdź __name__ == '__main__' i zignoruj inne nie wiarygodne szczegóły implementacji.

 3
Author: ddbug,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-08 11:06:08