
Bytearray – tworzy mutowalną sekwencję bajtów
Praca na danych binarnych pojawia się szybciej, niż zwykle zakłada początkujący programista. Odczyt plików, komunikacja po sieci, analiza ramek protokołów, operacje na obrazach, archiwach czy danych z urządzeń – wszędzie tam operujemy nie na znakach, ale na bajtach. Różnica między obiektem niemodyfikowalnym a takim, który można zmieniać w miejscu, ma wtedy realne znaczenie dla wydajności i czytelności kodu. W Pythonie właśnie do tego służy bytearray – tworzy mutowalną sekwencję bajtów.
Spis Treści
Dlaczego praca na bajtach jest inna niż operacje na zwykłych łańcuchach znaków
Łańcuch tekstowy str opisuje znaki Unicode, czyli warstwę logiczną tekstu. Bajty to warstwa niższa — konkretne wartości od 0 do 255 zapisane w pamięci lub przesyłane między systemami.
To ważne rozróżnienie. Napis „ABC” jako tekst i jako dane binarne nie są tym samym obiektem. Tekst wymaga kodowania, na przykład UTF-8, ASCII albo UTF-16. Bajty są już wynikiem tego kodowania.
Przykład:
- tekst:
"ABC" - bajty ASCII:
65 66 67 - zapis szesnastkowy:
41 42 43
Jeżeli program czyta nagłówek pliku PNG albo pakiet TCP, interesują go konkretne wartości bajtowe, nie litery.
Typ bytes w Pythonie przechowuje sekwencję bajtów, ale jest niemutowalny. Oznacza to, że po utworzeniu nie można zmienić pojedynczego elementu bez tworzenia nowego obiektu. Przy dużych buforach jest to kosztowne.
Tutaj pojawia się bytearray, który pozwala modyfikować dane w miejscu.
bytearray – tworzy mutowalną sekwencję bajtów i dlatego różni się od typu bytes
Najważniejsza cecha tego typu to mutowalność. Można zmieniać elementy, usuwać je, dopisywać nowe oraz wykonywać operacje podobne do pracy na liście liczb całkowitych.
Każdy element jest liczbą całkowitą z zakresu:
| Własność | Wartość |
|---|---|
| Minimalna wartość | 0 |
| Maksymalna wartość | 255 |
| Typ pojedynczego elementu | int |
To oznacza, że:
dane[0] = 65
jest poprawne, ale:
dane[0] = 300
spowoduje błąd ValueError.
Tworzenie obiektu można wykonać na kilka sposobów.
| Sposób | Przykład |
|---|---|
| Pusty bufor | bytearray() |
| Bufor o zadanym rozmiarze | bytearray(10) |
| Z listy liczb | bytearray([65, 66, 67]) |
| Z tekstu i kodowania | bytearray("ABC", "utf-8") |
| Z obiektu bytes | bytearray(b"ABC") |
Warto zauważyć, że bytearray(10) nie tworzy liczb od 1 do 10, tylko dziesięć bajtów o wartości zero.
Operacje podstawowe i mechanika działania indeksowania oraz modyfikacji danych
bytearray zachowuje się częściowo jak lista i częściowo jak bytes.
Można używać:
- indeksowania
- wycinków
append()extend()insert()pop()remove()reverse()
Przykłady najlepiej widać w prostym zestawieniu.
| Operacja | Python |
|---|---|
| Utworzenie | bufor = bytearray(b"ABC") |
| Odczyt elementu | print(bufor[0]) |
| Zmiana elementu | bufor[0] = 90 |
| Dopisanie bajtu | bufor.append(68) |
| Rozszerzenie | bufor.extend(b"EF") |
| Usunięcie | bufor.pop() |
Po zmianie:
bufor[0] = 90
wartość 65 zostaje zastąpiona przez 90, czyli znak Z.
To jest różnica praktyczna, nie tylko teoretyczna. Przy parsowaniu binarnego protokołu można poprawić fragment danych bez tworzenia nowego obiektu o rozmiarze kilku megabajtów.
Przykłady kodu w C, C++ i Python pokazujące podobną ideę pracy na buforze bajtowym
Sam bytearray jest typem Pythona, ale koncepcja mutowalnego bufora bajtowego istnieje w każdym języku.
| Język | Przykład |
|---|---|
| Python | bufor = bytearray(b"ABC")bufor[1] = 88print(bufor) |
| C | unsigned char bufor[] = {'A','B','C'};bufor[1] = 'X';printf("%s", bufor); |
| C++ | std::vector<unsigned char> bufor = {'A','B','C'};bufor[1] = 'X'; |
W C programista zarządza pamięcią samodzielnie. W Pythonie interpreter robi to automatycznie, ale logika pozostaje podobna: istnieje obszar pamięci, którego zawartość można zmieniać.
To właśnie sprawia, że bytearray jest naturalny dla osób pracujących wcześniej z C.
bytearray – tworzy mutowalną sekwencję bajtów podczas pracy z plikami, siecią i buforami danych
Najczęstsze zastosowania są bardzo praktyczne.
Odczyt i modyfikacja plików binarnych
Przy pracy z obrazami, archiwami ZIP, PDF albo własnym formatem binarnym często trzeba zmienić pojedynczy bajt nagłówka.
| Przykład | Python |
|---|---|
| Odczyt pliku | with open("plik.bin", "rb") as f:dane = bytearray(f.read()) |
| Modyfikacja | dane[0] = 255 |
| Zapis | with open("nowy.bin", "wb") as f:f.write(dane) |
Bufory sieciowe
Dane przychodzące z gniazda sieciowego nie zawsze są kompletne. Często trzeba budować bufor etapami.
| Przykład | Python |
|---|---|
| Bufor sieciowy | bufor = bytearray()bufor.extend(sock.recv(1024)) |
Komunikacja z urządzeniami
Przy UART, SPI, I2C albo komunikacji z mikrokontrolerami często wysyła się dokładnie określone bajty.
| Polecenie | Python |
|---|---|
| Ramka | ramka = bytearray([0xAA, 0x01, 0x05, 0xFF]) |
Tu nie operuje się na tekście. Liczy się dokładna wartość każdego bajtu.
Zależność między bytes, bytearray i pamięcią programu podczas wykonywania operacji
bytes jest niemutowalny, więc każda zmiana oznacza nowy obiekt.
Schemat wygląda tak:
| Typ | Zachowanie |
|---|---|
bytes | zmiana = nowy obiekt |
bytearray | zmiana = modyfikacja istniejącego obiektu |
Przy małych danych różnica jest niewielka. Przy buforze 100 MB wykonywanym tysiące razy dziennie różnica staje się kosztowna.
Wydajność zależy od scenariusza:
- odczyt bez zmian → często wystarczy
bytes - częste poprawki → lepszy
bytearray - budowanie danych etapami → zwykle
bytearray
Nie chodzi wyłącznie o szybkość. Chodzi też o przewidywalność kodu i mniejszą liczbę niepotrzebnych kopii danych.
Wzory i reprezentacje liczbowe potrzebne do zrozumienia zapisu bajtów
Programista pracujący z bajtami regularnie spotyka zapis dziesiętny, binarny i szesnastkowy.
| System | Wartość liczby 65 |
|---|---|
| Dziesiętny | 65 |
| Binarny | 01000001 |
| Szesnastkowy | 0x41 |
Zakres jednego bajtu wynika z prostego wzoru:
| Wzór | Znaczenie |
|---|---|
2^8 = 256 | liczba możliwych wartości |
0 ... 255 | realny zakres bajtu |
Dlatego jeden bajt nie przechowa wartości 300, ale dwa bajty już tak.
To jest istotne przy parsowaniu protokołów i struktur binarnych.
bytearray – tworzy mutowalną sekwencję bajtów a poprawne użycie zmniejsza liczbę błędów pamięci
Najczęstszy błąd początkujących to mieszanie tekstu i bajtów.
Nie działa:
bytearray("ABC")bo Python wymaga jawnego kodowania.
Poprawnie:
bytearray("ABC", "utf-8")Drugi problem to próba wpisania wartości spoza zakresu.
| Błąd | Skutek |
|---|---|
bufor[0] = 300 | ValueError |
bufor.append(-1) | ValueError |
Trzeci problem to założenie, że wynik indeksowania zwraca znak.
print(bufor[0])
zwraca liczbę całkowitą, nie znak.
Jeżeli potrzebny jest znak:
chr(bufor[0])
albo konwersja całego obiektu:
bufor.decode("utf-8")W praktyce właśnie tutaj najczęściej traci się czas: program działa, ale dane są interpretowane nie tak, jak zakłada autor.
FAQ
Czy bytearray jest szybszy od bytes
Nie zawsze. Do samego odczytu często wystarczy bytes. Przy częstych modyfikacjach bytearray zwykle jest lepszy, bo nie wymaga ciągłego tworzenia nowych obiektów.
Czy można zamienić bytearray na bytes
Tak.
| Operacja | Python |
|---|---|
| Konwersja | wynik = bytes(bufor) |
Przydaje się to, gdy biblioteka oczekuje dokładnie typu bytes.
Czy bytearray można sortować
Technicznie tak, ale w praktyce rzadko ma to sens. Bajty zwykle reprezentują strukturę danych, a nie wartości do porządkowania.
Czy bytearray nadaje się do dużych plików
Tak, ale trzeba uważać na pamięć RAM. Wczytanie całego pliku 2 GB nadal oznacza potrzebę posiadania odpowiedniej ilości pamięci operacyjnej.
Czy pojedynczy element jest typu bytes
Nie. Jest typu int, czyli liczbą od 0 do 255.
Czy można używać slice tak jak w listach
Tak.
| Operacja | Python |
|---|---|
| Fragment bufora | fragment = bufor[2:8] |
To bardzo wygodne przy analizie nagłówków plików i ramek sieciowych.
Krótkie zakończenie praktyczne
bytearray nie jest egzotycznym dodatkiem do języka, tylko narzędziem potrzebnym wszędzie tam, gdzie program styka się z rzeczywistymi danymi binarnymi. Im wcześniej programista nauczy się rozróżniać tekst od bajtów, tym mniej czasu straci później na błędy związane z kodowaniem, kopiowaniem pamięci i nieczytelnym debugowaniem. Przy pracy z plikami, protokołami i urządzeniami ten typ staje się po prostu codziennym narzędziem.
Źródło Foto: Freepik


