Last modified: June 30, 2022
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.
disModuł 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.
disAby 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).inspectModuł 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.
inspectOto 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.