Last modified: June 29, 2024
This article is written in: 馃嚨馃嚤
Kod bajtowy (ang. bytecode) w Pythonie to po艣rednia, niskopoziomowa reprezentacja kodu 藕r贸d艂owego, kt贸ra jest zrozumia艂a dla wirtualnej maszyny Pythona (Python Virtual Machine, PVM). Kiedy uruchamiamy skrypt Pythona, interpreter nie wykonuje bezpo艣rednio kodu 藕r贸d艂owego; zamiast tego, najpierw kompiluje go do kodu bajtowego. Ten proces kompilacji jest automatyczny i zazwyczaj niewidoczny dla programisty.
Kod bajtowy jest zoptymalizowan膮 wersj膮 kodu, kt贸ra mo偶e by膰 szybciej interpretowana przez PVM. Dzi臋ki temu wykonanie programu jest bardziej efektywne, poniewa偶 PVM operuje na kodzie bajtowym zamiast na kodzie 藕r贸d艂owym wysokiego poziomu. Kod bajtowy jest przeno艣ny mi臋dzy r贸偶nymi platformami, co oznacza, 偶e ten sam kod bajtowy mo偶e by膰 uruchamiany na r贸偶nych systemach operacyjnych z zainstalowanym interpreterem Pythona.
.pyc
w katalogu __pycache__
. Dzi臋ki temu przy ponownym uruchomieniu programu Python mo偶e u偶y膰 ju偶 skompilowanego kodu bajtowego, co przyspiesza uruchamianie aplikacji.Kod bajtowy sk艂ada si臋 z sekwencji instrukcji, z kt贸rych ka偶da reprezentuje podstawow膮 operacj臋, tak膮 jak:
LOAD_CONST
艂adowane s膮 sta艂e warto艣ci na stos.BINARY_ADD
, BINARY_MULTIPLY
wykonuj膮 operacje na warto艣ciach na stosie.JUMP_IF_FALSE
, RETURN_VALUE
steruj膮 przep艂ywem programu.STORE_NAME
, LOAD_NAME
operuj膮 na zmiennych.Rozwa偶my prosty kod:
def powitaj(imie):
print(f"Cze艣膰, {imie}!")
Kompiluj膮c t臋 funkcj臋 do kodu bajtowego i u偶ywaj膮c modu艂u dis
, otrzymamy:
2 0 LOAD_GLOBAL 0 (print)
2 LOAD_CONST 1 ('Cze艣膰, {}!')
4 LOAD_FAST 0 (imie)
6 FORMAT_VALUE 0
8 BUILD_STRING 2
10 CALL_FUNCTION 1
12 POP_TOP
14 LOAD_CONST 0 (None)
16 RETURN_VALUE
Ka偶da z tych instrukcji odpowiada konkretnemu dzia艂aniu w funkcji, takim jak 艂adowanie warto艣ci, wywo艂anie funkcji czy zwr贸cenie warto艣ci.
dis
Modu艂 dis
(disassembler) jest narz臋dziem wbudowanym w Pythona, kt贸re pozwala na dezasemblacj臋 kodu bajtowego. Umo偶liwia on analiz臋, w jaki spos贸b interpreter Pythona przekszta艂ca kod 藕r贸d艂owy na instrukcje kodu bajtowego.
dis
Aby u偶y膰 modu艂u dis
, nale偶y go zaimportowa膰:
from dis import dis
Nast臋pnie mo偶na przekaza膰 funkcj臋 lub inny obiekt do funkcji dis()
, aby wy艣wietli膰 jego kod bajtowy.
Rozwa偶my funkcj臋 sumuj膮c膮 dwie liczby:
def suma(a, b):
return a + b
dis(suma)
Wynik dezasemblacji:
2 0 LOAD_FAST 0 (a)
2 LOAD_FAST 1 (b)
4 BINARY_ADD
6 RETURN_VALUE
a
na stos operand贸w.b
na stos.We藕my bardziej z艂o偶ony przyk艂ad z u偶yciem p臋tli i warunk贸w:
def licz_parzyste(n):
suma = 0
for i in range(n):
if i % 2 == 0:
suma += i
return suma
dis(licz_parzyste)
Wynik dezasemblacji b臋dzie d艂u偶szy i zawiera膰 b臋dzie instrukcje obs艂uguj膮ce p臋tle (SETUP_LOOP
, FOR_ITER
), warunki (POP_JUMP_IF_FALSE
), operacje arytmetyczne (INPLACE_ADD
), itp.
Analizuj膮c ten kod bajtowy, mo偶emy zrozumie膰, jak Python implementuje p臋tle i warunki na niskim poziomie.
Modu艂 dis
pozwala r贸wnie偶 na analiz臋 dowolnych blok贸w kodu, nie tylko funkcji. Mo偶emy skompilowa膰 fragment kodu za pomoc膮 funkcji compile()
, a nast臋pnie zdezasemblowa膰 go.
compile()
Funkcja compile()
przekszta艂ca ci膮g znak贸w zawieraj膮cy kod 藕r贸d艂owy Pythona w obiekt kodu bajtowego.
code = compile(source, filename, mode)
'exec'
, 'eval'
, 'single'
).
from dis import dis
code = compile("""
x = 10
y = 20
z = x + y
print(z)
""", "<string>", "exec")
dis(code)
Wynik dezasemblacji poka偶e, jakie instrukcje s膮 wykonywane podczas inicjalizacji zmiennych, wykonywania operacji arytmetycznych i wywo艂ywania funkcji print
.
print
).inspect
Modu艂 inspect
w Pythonie dostarcza pot臋偶nych narz臋dzi do introspekcji, czyli badania obiekt贸w w czasie wykonywania programu. Pozwala na analiz臋 modu艂贸w, klas, funkcji, metod, ramek stosu i kodu 藕r贸d艂owego. Jest to szczeg贸lnie przydatne przy pracy z zewn臋trznymi bibliotekami, gdy chcemy zrozumie膰 ich wewn臋trzne dzia艂anie lub znale藕膰 odpowiedzi na pytania, kt贸rych nie ma w dokumentacji.
inspect
Oto poprawiona tabela w formacie Markdown, gdzie ostatni wiersz zosta艂 odpowiednio dostosowany:
Funkcja | Opis |
inspect.getsource(object) | Zwraca kod 藕r贸d艂owy obiektu jako string. Umo偶liwia zobaczenie implementacji funkcji, klasy czy metody. |
inspect.getdoc(object) | Zwraca dokumentacj臋 obiektu (docstring). Pozwala na szybki dost臋p do informacji o tym, co robi dany obiekt. |
inspect.getcomments(object) | Zwraca komentarze poprzedzaj膮ce definicj臋 obiektu. |
inspect.signature(object) | Zwraca obiekt Signature, kt贸ry reprezentuje podpis funkcji lub metody, w tym informacje o argumentach i warto艣ciach domy艣lnych. |
inspect.getmodule(object) | Zwraca modu艂, w kt贸rym obiekt zosta艂 zdefiniowany. |
inspect.getmembers(object, predicate=None) | Zwraca list臋 krotek (name, value) dla wszystkich atrybut贸w obiektu. Opcjonalnie mo偶na poda膰 predykat filtruj膮cy wyniki. |
inspect.isclass(object), inspect.isfunction(object), inspect.ismethod(object) | Funkcje kontrolne sprawdzaj膮ce, czy obiekt jest klas膮, funkcj膮 lub metod膮. |
Przeanalizujmy klas臋 Tk
z modu艂u tkinter
:
import inspect
import tkinter
# Pobranie kodu 藕r贸d艂owego klasy Tk
source = inspect.getsource(tkinter.Tk)
print("Kod 藕r贸d艂owy klasy Tk:")
print(source)
# Pobranie dokumentacji klasy Tk
doc = inspect.getdoc(tkinter.Tk)
print("\nDokumentacja klasy Tk:")
print(doc)
# Pobranie podpisu konstruktora klasy Tk
signature = inspect.signature(tkinter.Tk)
print("\nPodpis konstruktora klasy Tk:")
print(signature)
# Sprawdzenie, czy Tk jest klas膮
is_class = inspect.isclass(tkinter.Tk)
print(f"\nCzy tkinter.Tk jest klas膮? {is_class}")
# Pobranie cz艂onk贸w klasy Tk
members = inspect.getmembers(tkinter.Tk)
print("\nCz艂onkowie klasy Tk:")
for name, value in members:
print(f"{name}: {value}")
Tk
jest zaimplementowana, co mo偶e by膰 pomocne przy zrozumieniu mechanizm贸w tworzenia okien w aplikacjach GUI.Tk
, co jest u偶yteczne, gdy dokumentacja oficjalna jest niewystarczaj膮ca.Tk
, co jest przydatne przy tworzeniu instancji z odpowiednimi parametrami.Tk
, co pozwala na eksploracj臋 jej funkcjonalno艣ci.inspect
do dostarczania podpowiedzi i dokumentacji.unittest
czy pytest
, mog膮 u偶ywa膰 introspekcji do automatycznego odkrywania test贸w.inspect
do analizowania stosu wywo艂a艅 i zmiennych lokalnych.inspect.getsource()
dzia艂a tylko wtedy, gdy kod 藕r贸d艂owy jest dost臋pny. W przypadku bibliotek skompilowanych (np. napisanych w C), kod 藕r贸d艂owy mo偶e nie by膰 dost臋pny.exec
), inspect
mo偶e nie by膰 w stanie go przeanalizowa膰.Je艣li spr贸bujemy u偶y膰 inspect.getsource()
na wbudowanej funkcji, np.:
import inspect
print(inspect.getsource(len))
Otrzymamy b艂膮d OSError
, poniewa偶 funkcja len
jest zaimplementowana w C i nie ma dost臋pnego kodu 藕r贸d艂owego w Pythonie.