Python kodunun -m seçeneğiyle veya değil çalıştırılması


111

Python yorumlayıcısının "Kitaplık modülü modülünü komut dosyası olarak çalıştırır" -m modül seçeneği vardır .

Bu python koduyla a.py:

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

Almak python -m aiçin test ettim

"" <-- Empty String
__main__

oysa python a.pydöner

None <-- None
__main__

Bana göre bu iki çağrı, __package__ 'ın -m seçeneği ile çağrıldığında None olmaması dışında aynı görünüyor.

İlginç bir şekilde, bir.pyc almak için derlenen python modülüyle python -m runpy aaynı şeyi python -m aalıyorum.

Bu çağrılar arasındaki (pratik) fark nedir? Aralarında artı ve eksiler var mı?

Ayrıca, David Beazley'in Python Temel Referansı bunu " -m seçeneği, ana betiğin yürütülmesinden önce __main__ modülünün içinde çalışan bir betik olarak bir kitaplık modülünü çalıştırır " şeklinde açıklar . Bu ne demek?

Yanıtlar:


169

Kullandığınızda -mkomut satırı işareti , Python bir modül ithal edecek veya paketi sizin için daha sonra bir komut dosyası olarak çalıştırın. -mBayrağı kullanmadığınızda, adlandırdığınız dosya sadece bir betik olarak çalıştırılır .

Bir paketi çalıştırmaya çalıştığınızda ayrım önemlidir. Arasında büyük bir fark var:

python foo/bar/baz.py

ve

python -m foo.bar.baz

ikinci durumda olduğu gibi foo.bar, ithal edilir ve göreli ithalatlar foo.bar, başlangıç ​​noktası olarak doğru şekilde çalışacaktır .

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__

Sonuç olarak, Python, -manahtarı kullanırken paketleri gerçekten önemsemelidir . Normal bir senaryo asla olmak , böylece bir paket __package__olarak ayarlanır None.

Ama bir paket veya modül çalıştırmak içeride olan bir paket -mve şimdi en azından orada bir olasılık böylece, bir paketin __package__değişken bir dize değerine ayarlanır; Yukarıdaki gösteride foo.bar, bir paketin içinde olmayan düz modüller için, boş bir dizeye ayarlanmıştır.

__main__ Modüle gelince ; Python, normal bir modül gibi çalıştırılan komut dosyalarını içe aktarır. Global ad alanını tutmak için yeni bir modül nesnesi oluşturulur sys.modules['__main__']. Bu nedir __name__bu yapıda bir anahtardır, değişken ifade eder.

Paketler için, bir __main__.pymodül oluşturabilir ve bunu çalışırken çalıştırabilirsiniz python -m package_name; aslında sen tek yolu olduğunu edebilir bir komut dosyası olarak bir paket çalıştırın:

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

Bu nedenle, bir paketi çalıştırmak için adlandırırken -m, Python __main__o pakette bulunan bir modülü arar ve bunu bir betik olarak yürütür. Adı daha sonra hala olarak ayarlanır __main__ve modül nesnesi hala içinde saklanır sys.modules['__main__'].


1
Komut aslında ne anlama PYTHONPATH=test python -m foo.bargeliyor? Ayrıntılı olarak açıklar mısınız lütfen?
Andriy

3
@Andriy: PYTHONPATHbir ortam değişkeni belirler; Python'un içe aktarırken modülleri arayacağı dizinler dizisini genişletir; burada testdizini o seriye ekler . Aynı komut satırına koyarak, yalnızca o tek pythonkomuta uygulanır. -mPython'a belirli bir modülü çalıştırmışsınız gibi içe aktarmasını söyler import foo.bar. Ancak, __main__bu anahtarı kullandığınızda Python, bir paket içindeki bir modülü komut dosyası olarak otomatik olarak çalıştırır .
Martijn Pieters

1
having to use -m always is not that user-.friendly.Bence karıştırmak ve kullanmamak -mdaha az kullanıcı dostu.
Simin Jie

1
@SiminJie: komut dosyaları rastgele herhangi bir yolda açılabilir ve daha sonra üst dizini modül arama yoluna eklenir. -myalnızca geçerli dizin veya arama yolunda önceden kayıtlı olan dizinler için çalışır. Benim amacım buydu. -mbu kullanılabilirlik sorunu için son kullanıcılara verdiğiniz bir şey değil.
Martijn Pieters

1
@ flow2k: from Photos import ...Şikayet edeceğim demek istiyorum . Öyle olur import Photos.<something>. import Photosyalnızca Python ad alanlı paketleri desteklediği için çalışır (burada iki ayrı dağıtım ayrı ayrı sağlar Photos.foove Photos.barbağımsız olarak yönetilebilirler).
Martijn Pieters

25

Python kodunun -m seçeneğiyle veya değil çalıştırılması

-mBayrağı kullanın .

Bir komut dosyanız olduğunda sonuçlar hemen hemen aynıdır, ancak bir paket geliştirdiğinizde, -mbayrak olmadan, paketteki bir alt paket veya modülü ana giriş olarak çalıştırmak istiyorsanız, içe aktarımların düzgün çalışmasını sağlamanın bir yolu yoktur. programına işaret et (ve inan bana denedim.)

Dokümanlar

Gibi -M bayrak dokümanlar söz hakkından:

Adlandırılmış modül için sys.path'i arayın ve içeriğini __main__modül olarak çalıştırın .

ve

-C seçeneğinde olduğu gibi, mevcut dizin sys.path'in başlangıcına eklenecektir.

yani

python -m pdb

kabaca eşdeğerdir

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

(mevcut dizininizde pdb.py adında bir paketiniz veya komut dosyanız olmadığı varsayılarak)

Açıklama:

Davranış, komut dosyalarına "kasıtlı olarak benzer" yapılır.

Birçok standart kütüphane modülü, çalıştırıldıklarında bir komut dosyası olarak çağrılan kodu içerir. Bir örnek, timeit modülüdür:

Bazı python kodlarının bir modül olarak çalıştırılması amaçlanmıştır : (Bu örneğin komut satırı seçeneği doc örneğinden daha iyi olduğunu düşünüyorum)

$ 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

Ve Python 2.4 için sürüm notu vurgularından :

-M komut satırı seçeneği - python -m modulename, standart kitaplıkta bir modül bulur ve onu çağırır. Örneğin python -m pdb , eşdeğerdirpython /usr/lib/python2.4/pdb.py

Takip Sorusu

Ayrıca, David Beazley'in Python Temel Referansı, bunu "-m seçeneği __main__, ana betiğin yürütülmesinden önce modülün içinde çalışan bir betik olarak bir kitaplık modülünü çalıştırır" şeklinde açıklar .

Bu, import deyimi ile arayabileceğiniz herhangi bir modülün programın giriş noktası olarak çalıştırılabileceği anlamına gelir - eğer bir kod bloğu varsa, genellikle sonuna yakın bir kod bloğu varsa if __name__ == '__main__':.

-m geçerli dizini yola eklemeden:

Burada başka bir yerde bir yorum şöyle diyor:

-M seçeneğinin geçerli dizini sys.path'e eklemesi de kesinlikle bir güvenlik sorunudur (bkz: önyükleme saldırısı). Bu davranış, Windows'taki kitaplık arama sırasına benzer (yakın zamanda sağlamlaştırılmadan önce). Python'un eğilimi takip etmemesi ve eklemeyi devre dışı bırakmak için basit bir yol sunmaması üzücü. sys.path'e

Bu olası sorunu gösterir - (pencerelerde tırnak işaretlerini kaldırın):

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

-IBunu üretim ortamları için kilitlemek için bayrağı kullanın (3.4 sürümünde yeni):

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

dan docs :

-I

Python'u yalıtılmış modda çalıştırın. Bu aynı zamanda -E ve -s anlamına gelir. Yalıtılmış modda sys.path ne komut dosyasının dizinini ne de kullanıcının site paketleri dizinini içerir. Tüm PYTHON * ortam değişkenleri de göz ardı edilir. Kullanıcının kötü amaçlı kod enjekte etmesini önlemek için başka kısıtlamalar da getirilebilir.

Ne anlama geliyor __package__?

Bu, özellikle bu soruyla ilgili olmayan açık göreli içe aktarımlara olanak tanır - bu yanıta bakın: Python'daki "__package__" özniteliğinin amacı nedir?


-M anahtarı kullanıldığında sys.path'e hangi yol eklenir?
değişken

Bunu zaten alıntı yaptım, "-c seçeneğinde olduğu gibi, mevcut dizin sys.path'in başlangıcına eklenecektir." ama alıntının ne anlama geldiğini açıkladım.
Aaron Hall

D: \ test dizininde - python -m foo.bar.boo komutunu çalıştırdığımı varsayalım, sonra bu python kurulum klasörünü veya D: \ test dizinini sys.path'e ekleyecek mi? Anladığım kadarıyla, sys.path'e d: \ test ekleyecek, foo.bar'ı içe aktaracak ve boo betiğini çalıştıracak
değişken

@variable - evet, deneyin.
Aaron Hall

1

Bir modülü (veya paketi) -m ile komut dosyası olarak çalıştırmanın ana nedeni, özellikle Windows'ta dağıtımı basitleştirmektir. Komut dosyalarını, PATH veya ~ / .local gibi genel yürütülebilir dizinleri kirletmek yerine, modüllerin normalde gittiği Python kitaplığında aynı yere kurabilirsiniz (kullanıcı başına komut dosyaları dizini Windows'ta bulmak gülünç derecede zordur).

Sonra -m yazarsınız ve Python betiği otomatik olarak bulur. Örneğin, python -m piponu çalıştıran Python yorumlayıcısının aynı örneği için doğru pip'i bulacaktır. -M olmadan, eğer kullanıcı birden fazla Python sürümüne sahipse, hangisi "global" pip olur?

Kullanıcı, komut satırı betikleri için "klasik" giriş noktalarını tercih ederse, bunlar PATH'de herhangi bir yere küçük betikler olarak kolayca eklenebilir veya pip bunları kurulum sırasında setup.py'deki entry_points parametresiyle oluşturabilir.

Bu nedenle, __name__ == '__main__'diğer güvenilir olmayan uygulama ayrıntılarını kontrol edin ve göz ardı edin .


-M seçeneğinin aynı zamanda geçerli dizini sys.path'e eklemesi de açıkça bir güvenlik sorunudur (bkz: önyükleme saldırısı ). Bu davranış, Windows'taki kitaplık arama sırasına benzer (yakın zamanda sağlamlaştırılmadan önce). Python'un eğilimi takip etmemesi ve eklemeyi devre dışı bırakmak için basit bir yol sunmaması üzücü. sys.path için.
2016
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.