Jeśli interpretuje się Python, jakie są pliki .pyc?

1083

Dano mi do zrozumienia, że ​​Python jest językiem interpretowanym ...
Jednak gdy patrzę na mój kod źródłowy Python , widzę .pycpliki, które Windows identyfikuje jako „Skompilowane pliki Pythona”.

Skąd one pochodzą?

froadie
źródło
3
Aby uzyskać uzasadnienie, patrz stackoverflow.com/questions/11433579/ ... Jednym słowem: prędkość.
user7610,
Czy to znaczy, że nawet python ma napis „Napisz raz, uruchom gdziekolwiek” tak jak Java.
Mrak Vladar
2
@MrakVladar Nawet Java to „Napisz raz, uruchom gdziekolwiek [masz JVM]”. Python nie jest inny; „działa wszędzie tam, gdzie masz maszynę wirtualną Python”. Duża różnica polega na tym, że większość implementacji Pythona łączy kompilator i interpreter w jeden plik wykonywalny, zamiast rozdzielać je jak javai javac.
chepner

Odpowiedzi:

660

Zawierają kod bajtowy , do którego interpreter języka Python kompiluje źródło. Ten kod jest następnie wykonywany przez maszynę wirtualną Pythona.

Dokumentacja Pythona wyjaśnia następującą definicję:

Python jest językiem interpretowanym, w przeciwieństwie do skompilowanego, chociaż rozróżnienie może być rozmyte z powodu obecności kompilatora kodu bajtowego. Oznacza to, że pliki źródłowe mogą być uruchamiane bezpośrednio, bez jawnego tworzenia pliku wykonywalnego, który jest następnie uruchamiany.

rozwijać
źródło
10
Ciekawe dzięki. Czy więc Python jest uważany za język wyłącznie interpretowany?
froadie
194
@froadie: język nie jest „interpretowany” ani „kompilowany” jako taki. Realizacja specyficznych może być interpreter lub kompilator (lub hybrydowy lub kompilator JIT).
Joachim Sauer
30
Jeden test „skompilowanego”: czy jest skompilowany z rzeczywistymi instrukcjami maszyny? Kod bajtowy Pythona nie jest instrukcją maszynową ani instrukcją Java JVM, więc żaden z tych języków nie jest kompilowany na podstawie tej definicji. Ale oba są „skompilowane” do pośredniego kodu „abstrakcyjnej maszyny” i oba są znacznie szybsze niż uruchamianie programu poprzez mniej więcej bezpośrednią interpretację kodu źródłowego (co robi old-school BASIC).
greggo
20
Być pedantycznym, „skompilowany” oznacza „przetłumaczony”. Python jest następnie kompilowany do kodu bajtowego. AFAIK, tylko Bash jest naprawdę interpretowany, wszystkie inne popularne „interpretowane” języki są kompilowane do kodu bajtowego.
bfontaine,
13
W rzeczywistości są to instrukcje maszynowe, a nie natywne instrukcje maszynowe dla fizycznego procesora hosta. Dlatego nazywamy to maszyną wirtualną? Naprawdę jak Esperanto dla języka asemblera. Obecnie mamy nawet natywny kod fikcyjnych (ale nadal emulowanych) procesorów (wysiłek Mojanga, aby zainteresować dzieciaków). Rexx został (lub mógłby być) prawdziwie zinterpretowany, a BAT i CMD (i DCL) zostały zinterpretowane.
mckenzm,
994

Dano mi do zrozumienia, że ​​Python jest językiem interpretowanym ...

Ten popularny mem jest niepoprawny, a raczej oparty na niezrozumieniu (naturalnego) poziomu języka: podobny błąd polegałby na stwierdzeniu, że „Biblia jest książką w twardej oprawie”. Pozwól mi wyjaśnić to porównanie ...

„Biblia” jest „książką” w tym sensie, że jest klasą (rzeczywistych przedmiotów fizycznych zidentyfikowanych jako) książki; książki oznaczone jako „kopie Biblii” powinny mieć ze sobą coś wspólnego (treść, chociaż nawet te mogą być w różnych językach, z różnymi akceptowalnymi tłumaczeniami, poziomami przypisów i innymi adnotacjami) - jednak te książki są doskonale może się różnić w niezliczonych aspektach, które nie są uważane za podstawowe - rodzaj oprawy, kolor oprawy, czcionka (-y) użyte w druku, ewentualne ilustracje, szerokie zapisywalne marginesy lub nie, liczby i rodzaje wbudowanych zakładek , i tak dalej i tak dalej.

Jest całkiem możliwe, że typowy druk Biblii rzeczywiście byłby oprawiony w twardą oprawę - w końcu jest to książka, którą zwykle należy czytać w kółko, dodając zakładki w kilku miejscach, przeglądając w poszukiwaniu wskazówek dotyczących rozdziałów i wierszy , itp. itd., a dobre oprawienie w twardej oprawie może wydłużyć daną kopię przy takim użyciu. Są to jednak przyziemne (praktyczne) kwestie, których nie można użyć do ustalenia, czy dany faktyczny obiekt książki jest kopią Biblii, czy nie: wydruk w miękkiej oprawie jest całkowicie możliwy!

Podobnie, Python jest „językiem” w sensie definiowania klasy implementacji językowych, które muszą być podobne pod pewnymi podstawowymi względami (składnia, większość semantyki, z wyjątkiem tych części tych, w których wyraźnie mogą się różnić), ale są w pełni dozwolone różnicować w każdym szczególe „implementacji” - w tym w tym, jak radzą sobie z plikami źródłowymi, które otrzymali, czy kompilują źródła do niektórych formularzy niższego poziomu (a jeśli tak, to w jakiej formie - i czy zapisują takie skompilowane formularze, na dysk lub gdzie indziej), w jaki sposób wykonują te formularze i tak dalej.

Klasyczna implementacja, CPython, jest często nazywana po prostu „Python” - w skrócie - ale jest to tylko jedna z kilku implementacji o jakości produkcyjnej, równolegle z IronPython Microsoftu (który kompiluje się do kodów CLR, tj. „.NET”), Jython (który kompiluje się do kodów JVM), PyPy (który jest napisany w samym Pythonie i może kompilować do ogromnej różnorodności formularzy „zaplecza”, w tym języka maszynowego „just-in-time”). Wszystkie są Pythonem (== „implementacje języka Python”), tak jak wiele powierzchownie różnych obiektów książkowych może być Biblią (== „kopie Biblii”).

Jeśli interesuje Cię konkretnie CPython: kompiluje pliki źródłowe w specyficzną dla Pythona formę niższego poziomu (znaną jako „kod bajtowy”), robi to automatycznie, gdy jest to potrzebne (gdy nie ma pliku kodu bajtowego odpowiadającego plikowi źródłowemu lub plik kodu bajtowego jest starszy niż kod źródłowy lub skompilowany w innej wersji języka Python), zwykle zapisuje pliki kodu bajtowego na dysku (aby uniknąć ich ponownej kompilacji w przyszłości). OTOH IronPython zwykle kompiluje się do kodów CLR (zapisując je na dysku lub nie, zależnie) i Jython do kodów JVM (zapisując je na dysku lub nie - użyje .classrozszerzenia, jeśli je zapisze).

Te formularze niższego poziomu są następnie wykonywane przez odpowiednie „maszyny wirtualne” zwane również „tłumaczami” - odpowiednio CPython VM, środowisko uruchomieniowe .Net, Java VM (inaczej JVM).

W tym sensie (co robią typowe implementacje) Python jest „językiem interpretowanym” tylko wtedy, gdy C # i Java to: wszystkie mają typową strategię implementacji polegającą na wygenerowaniu kodu bajtowego, a następnie uruchomieniu go za pomocą maszyny wirtualnej / interpretera .

Bardziej prawdopodobne jest skoncentrowanie się na tym, jak „ciężki”, powolny i uroczysty jest proces kompilacji. CPython został zaprojektowany do kompilacji tak szybko, jak to możliwe, tak lekki, jak to możliwe, przy możliwie jak najmniejszej ceremonii - kompilator bardzo mało sprawdza i optymalizuje błędy, dzięki czemu może działać szybko i przy małej ilości pamięci, co z kolei pozwala mu uruchamiać się automatycznie i przejrzyście, gdy zajdzie taka potrzeba, bez konieczności nawet uświadomienia sobie, że przez większość czasu trwa kompilacja. Java i C # zazwyczaj akceptują więcej pracy podczas kompilacji (a zatem nie wykonują automatycznej kompilacji) w celu dokładniejszego sprawdzania błędów i przeprowadzania większej optymalizacji. To kontinuum szarych łusek, a nie sytuacja czarno-biała,

Alex Martelli
źródło
2
Piękna odpowiedź. Tylko mała poprawka do ostatniego akapitu: Python został zaprojektowany tak, aby skompilować tak szybko, jak to możliwe (itp.). Tym razem jest to naprawdę język, z brakiem statycznego systemu typów i tym podobnych. Kiedy ludzie mówią o językach „interpretowanych”, zwykle mają na myśli języki „dynamiczne”.
Elazar
1
@Elazar, w rzeczywistości inne implementacje Pythona, takie jak PyPy, których kompilacji nie spieszy się, udaje się przeprowadzić dokładniejszą analizę wymaganą przez brak statycznego pisania i wygenerować kompilację na czas do kodu maszynowego (przyspieszając w ten sposób wielokrotnie uruchamiane długo działające programy).
Alex Martelli,
Gdzie pasuje tutaj Cython? Czy uważasz, że jest to inny język, czy jest to implementacja w języku Python? Ponadto, czy ten mem „interpretowany” vs. skompilowany jest może tylko pomyłką terminologiczną, ponieważ maszynę wirtualną Pythona często określa się mianem „tłumacza”? Równie poprawne byłoby wywołanie JVM lub interpreterów środowiska wykonawczego .NET. Obaj interpretują głównie kod bajtowy na kod maszynowy JIT (z pewnymi wyjątkami optymalizacji buforowania)
Davos
181

Nie ma czegoś takiego jak interpretowany język. To, czy używany jest interpreter, czy kompilator, jest czystą cechą implementacji i nie ma absolutnie nic wspólnego z językiem.

Każdy język może być implementowany przez tłumacza lub kompilatora. Zdecydowana większość języków ma co najmniej jedną implementację każdego typu. (Na przykład istnieją interpretery dla C i C ++ oraz kompilatory dla JavaScript, PHP, Perl, Python i Ruby.) Poza tym większość współczesnych implementacji językowych faktycznie łączy zarówno interpreter, jak i kompilator (lub nawet wiele kompilatorów).

Język to tylko zbiór abstrakcyjnych reguł matematycznych. Tłumacz jest jedną z kilku konkretnych strategii wdrażania dla języka. Ci dwaj żyją na zupełnie różnych poziomach abstrakcji. Gdyby angielski był językiem pisanym na maszynie, termin „język interpretowany” byłby błędem pisowni. Stwierdzenie „Python jest językiem interpretowanym” jest nie tylko fałszywe (ponieważ fałszywe oznaczałoby, że instrukcja ma sens, nawet jeśli jest niepoprawne), po prostu nie ma sensu , ponieważ języka nigdy nie można zdefiniować jako „interpretowane”.

W szczególności, jeśli spojrzysz na obecnie istniejące implementacje Pythona, są to stosowane przez nich strategie implementacyjne:

  • IronPython: kompiluje do drzew DLR, które następnie DLR kompiluje do kodu bajtowego CIL. To, co stanie się z bajtem kodu CIL, zależy od tego, na którym systemie CLI VES używasz, ale Microsoft .NET, GNU Portable.NET i Novell Mono ostatecznie skompilują go do natywnego kodu maszynowego.
  • Jython: interpretuje kod źródłowy Pythona, dopóki nie zidentyfikuje ścieżek gorącego kodu, które następnie kompiluje do kodu bajtowego JVML. To, co stanie się z kodem bajtowym JVML, zależy od używanej maszyny JVM. Maxine bezpośrednio skompiluje go do niezoptymalizowanego kodu natywnego, dopóki nie zidentyfikuje ścieżek gorącego kodu, które następnie przekompiluje do zoptymalizowanego kodu natywnego. HotSpot najpierw zinterpretuje kod bajtowy JVML, a następnie ostatecznie skompiluje ścieżki gorącego kodu do zoptymalizowanego kodu maszynowego.
  • PyPy: kompiluje się do kodu bajtowego PyPy, który następnie jest interpretowany przez maszynę wirtualną PyPy, dopóki nie zidentyfikuje ścieżek do gorącego kodu, które następnie kompiluje w natywny kod, kod bajtowy JVML lub kod bajtowy CIL, w zależności od platformy, na której pracujesz.
  • CPython: kompiluje się do kodu bajtowego CPython, który następnie interpretuje.
  • Python bez stosu: kompiluje się do kodu bajtowego CPython, który następnie interpretuje.
  • Unladen Swallow: kompiluje do kodu bajtowego CPython, który następnie interpretuje, dopóki nie zidentyfikuje ścieżek gorącego kodu, które następnie kompiluje do LLVM IR, które kompilator LLVM następnie kompiluje do natywnego kodu maszynowego.
  • Cython: kompiluje kod Pythona do przenośnego kodu C, który jest następnie kompilowany za pomocą standardowego kompilatora C.
  • Nuitka: kompiluje kod Pythona do zależnego od maszyny kodu C ++, który jest następnie kompilowany za pomocą standardowego kompilatora C.

Możesz zauważyć, że każda implementacja na tej liście (plus kilka innych, o których nie wspomniałem, takich jak tinypy, Shedskin lub Psyco) ma kompilator. W rzeczywistości, o ile mi wiadomo, obecnie nie ma implementacji w języku Python, która jest interpretowana czysto, nie jest planowana taka implementacja i nigdy nie było takiej implementacji.

Termin „język interpretowany” nie ma sensu, nawet jeśli interpretujesz go jako „język z interpretowaną implementacją”, jest to oczywiście nieprawda. Ktokolwiek ci to powiedział, oczywiście nie wie o czym mówi.

W szczególności .pycpliki, które widzisz, są buforowanymi plikami kodów bajtowych wyprodukowanymi przez CPython, Stackless Python lub Unladen Swallow.

Jörg W Mittag
źródło
5
Podstawowe elementy starej szkoły, takie jak MSBASIC, nie miały formy pośredniej. Program został zinterpretowany bezpośrednio z postaci źródłowej (lub w pobliżu źródła, formy, w której słowa kluczowe były reprezentowane przez 1-bajtowe tokeny, a wiersze nr 2-bajtowymi liczbami całkowitymi, ale reszta to tylko ASCII). Tak więc „goto” zajęłoby różną ilość czasu w zależności od tego, ile linii źródłowych musiał przeszukać, szukając pasującego miejsca docelowego. Wyrażenia takie jak * b-2 * cos (x) były skutecznie ponownie analizowane za każdym razem, gdy były wykonywane.
greggo
4
@greggo: A jeśli chcesz iść jeszcze bardziej oldschoolowy, oryginalna wersja BASIC była natywnym kompilatorem kodu. To powinno udowodnić, jak absurdalne jest pojęcie „skompilowanego” lub „zinterpretowanego” języka.
Jörg W Mittag
Dziękujemy za wyjaśnienie, jak zachowują się różne kompilatory / interpretatory Pythona. Zastanawiam się, czy istnieją dobre kompilatory Pythona, które generują jeszcze wydajne C lub JavaScript. Wydaje się to bardzo wykonalne, może nie do masowej konsumpcji, ale do rozsądnego podzbioru Pythona. Zastanawiam się także, czym jest Cython.
personal_cloud
Cython został wspomniany w SciPy 2009, ale mogę ci wybaczyć, że nie wiedziałeś o tym w 2010 roku (tutaj jestem w 2017 roku, dopiero teraz się o nim uczę). Nadal powinniśmy znaleźć przykład JavaScript ... Jython nie ma dla mnie sensu (czy Java nie była już martwa do 2009 roku? No cóż, może nie ... Zwiększenie C ++ nie było wtedy tak dobre)
personal_cloud
1
@personal_cloud: Nie bardzo podążam za twoim komentarzem. Tak, oczywiście, wiem o Cython, ale co to ma z tym wspólnego? To nie jest implementacja Pythona, to zupełnie inny język. Ponadto naprawdę nie jest trudno znaleźć przykład JavaScript, w rzeczywistości wszystkie obecnie istniejące implementacje JavaScript głównego nurtu mają kompilatory. Wreszcie Jython jest implementacją Pythona, podobnie jak każda inna implementacja Pythona. Jest to implementacja języka na platformie Java, podobnie jak każda inna implementacja języka na platformie Java.
Jörg W Mittag
61

Są one tworzone przez interpreter Pythona podczas .pyimportowania pliku i zawierają „skompilowany kod bajtowy” importowanego modułu / programu, przy czym chodzi o to, że „tłumaczenie” z kodu źródłowego na kod bajtowy (co należy zrobić tylko raz) można pominąć w kolejnych imports, jeśli .pycjest on nowszy niż odpowiedni .pyplik, co przyspiesza nieco uruchomienie. Ale nadal jest interpretowane.

Tim Pietzcker
źródło
10
Prawdziwe. Tyle, że wiele podstawowych bibliotek Pythona jest napisanych w C. Tak więc niektóre części uruchamiane w Pythonie są interpretowane, a części w C. Można zrobić to samo z własnymi bitami kodu wrażliwymi na wydajność.
bwawok
44

Aby przyspieszyć ładowanie modułów, Python buforuje skompilowaną zawartość modułów w .pyc.

CPython kompiluje swój kod źródłowy w „kod bajtowy”, a ze względu na wydajność buforuje ten kod bajtowy w systemie plików, ilekroć plik źródłowy ulegnie zmianie. To sprawia, że ​​ładowanie modułów Pythona jest znacznie szybsze, ponieważ można ominąć fazę kompilacji. Gdy plik źródłowy to foo.py, CPython buforuje kod bajtu w pliku foo.pyc tuż obok źródła.

W Python3 mechanizm importu Pythona jest rozszerzony o zapisywanie i wyszukiwanie plików pamięci podręcznej kodów bajtów w jednym katalogu w każdym katalogu pakietu Pythona. Ten katalog będzie nazywał się __pycache__.

Oto schemat blokowy opisujący sposób ładowania modułów:

wprowadź opis zdjęcia tutaj

Po więcej informacji:

ref: PEP3147
ref: „Skompilowane” pliki Pythona

hxysayhi
źródło
38

TO JEST DLA POCZĄTKUJĄCYCH,

Python automatycznie kompiluje skrypt do skompilowanego kodu, tzw. Kodu bajtowego, przed jego uruchomieniem.

Uruchomienie skryptu nie jest uważane za import i nie zostanie utworzony plik .pyc.

Na przykład, jeśli masz plik skryptu abc.py, który importuje inny moduł xyz.py , po uruchomieniu abc.py , xyz.pyc zostanie utworzony od czasu importu xyz, ale od abc nie zostanie utworzony plik abc.pyc . py nie jest importowane.

Jeśli musisz utworzyć plik .pyc dla modułu, który nie jest importowany, możesz użyć modułów py_compilei compileall.

py_compileModuł ręcznie opracowania każdego modułu. Jednym ze sposobów jest py_compile.compileinteraktywne użycie funkcji w tym module:

>>> import py_compile
>>> py_compile.compile('abc.py')

Spowoduje to zapisanie pliku .pyc w tej samej lokalizacji, co abc.py (możesz to zmienić przy pomocy parametru opcjonalnego cfile).

Możesz także automatycznie skompilować wszystkie pliki w katalogu lub katalogach za pomocą modułu kompilacji.

python -m compileall

Jeśli nazwa katalogu (bieżący katalog w tym przykładzie) zostanie pominięta, moduł kompiluje wszystko, co znaleziono sys.path

MAX
źródło
6
i jaka jest korzyść z kompilacji, aby uzyskać abc.py?
Saher Ahwal
@ SaherAhwal Jedną z korzyści, o których mogę myśleć, jest sprawdzanie składni.
Yi Bao
20

Python (przynajmniej najczęstsza jego implementacja) postępuje według schematu kompilowania oryginalnego źródła do kodów bajtów, a następnie interpretacji kodów bajtów na maszynie wirtualnej. Oznacza to (ponownie, najczęstszą implementację), że nie jest to czysty interpreter ani kompilator.

Z drugiej strony proces kompilacji jest w większości ukryty - pliki .pyc są traktowane jak pamięć podręczna; przyspieszają, ale zwykle nie musisz wcale o nich pamiętać. Automatycznie unieważnia je i ładuje ponownie (ponownie kompiluje kod źródłowy) w razie potrzeby na podstawie znaczników czasu / daty pliku.

Mniej więcej raz widziałem problem z tym, kiedy skompilowany plik kodu bajtowego w jakiś sposób dostał znacznik czasu w przyszłości, co oznaczało, że zawsze wyglądał nowiej niż plik źródłowy. Ponieważ wyglądał na nowszy, plik źródłowy nigdy nie został ponownie skompilowany, więc niezależnie od wprowadzonych zmian, zostały one zignorowane ...

Jerry Coffin
źródło
12

Plik * .py Pythona jest tylko plikiem tekstowym, w którym piszesz kilka wierszy kodu. Gdy próbujesz uruchomić ten plik, powiedz „python nazwa_pliku.py”

To polecenie wywołuje maszynę wirtualną w języku Python. Maszyna wirtualna Python ma 2 komponenty: „kompilator” i „interpreter”. Tłumacz nie może bezpośrednio odczytać tekstu w pliku * .py, dlatego ten tekst jest najpierw konwertowany na kod bajtowy, który jest skierowany do PVM (nie sprzętowo, ale PVM) . PVM wykonuje ten bajtowy kod. Generowany jest również plik * .pyc w ramach jego uruchamiania, który wykonuje operację importowania pliku w powłoce lub w innym pliku.

Jeśli ten plik * .pyc jest już wygenerowany, to przy następnym uruchomieniu / uruchomieniu pliku * .py system bezpośrednio ładuje plik * .pyc, który nie będzie wymagał żadnej kompilacji (pozwoli to zaoszczędzić kilka cykli pracy procesora).

Po wygenerowaniu pliku * .pyc plik * .py nie jest potrzebny, chyba że zostanie poddany edycji.

Vishal Mopari
źródło
7

Kod Python przechodzi przez 2 etapy. Pierwszy krok kompiluje kod do plików .pyc, które w rzeczywistości są kodem bajtowym. Następnie ten plik .pyc (kod bajtowy) jest interpretowany przy użyciu interpretera CPython. Proszę odnieść się do tego linku. Tutaj proces kompilacji i wykonywania kodu wyjaśniono w prosty sposób.

Tango
źródło