Dano mi do zrozumienia, że Python jest językiem interpretowanym ...
Jednak gdy patrzę na mój kod źródłowy Python , widzę .pyc
pliki, które Windows identyfikuje jako „Skompilowane pliki Pythona”.
Skąd one pochodzą?
java
i javac
.
Dano mi do zrozumienia, że Python jest językiem interpretowanym ...
Jednak gdy patrzę na mój kod źródłowy Python , widzę .pyc
pliki, które Windows identyfikuje jako „Skompilowane pliki Pythona”.
Skąd one pochodzą?
java
i javac
.
Odpowiedzi:
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.
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 .class
rozszerzenia, 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,
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:
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 .pyc
pliki, które widzisz, są buforowanymi plikami kodów bajtowych wyprodukowanymi przez CPython, Stackless Python lub Unladen Swallow.
Są one tworzone przez interpreter Pythona podczas .py
importowania 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 import
s, jeśli .pyc
jest on nowszy niż odpowiedni .py
plik, co przyspiesza nieco uruchomienie. Ale nadal jest interpretowane.
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:
Po więcej informacji:
ref: PEP3147
ref: „Skompilowane” pliki Pythona
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_compile
i compileall
.
py_compile
Moduł ręcznie opracowania każdego modułu. Jednym ze sposobów jest py_compile.compile
interaktywne 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
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 ...
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.
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.