Bytearray
Kodowanie

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.

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 elementuint

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óbPrzykład
Pusty buforbytearray()
Bufor o zadanym rozmiarzebytearray(10)
Z listy liczbbytearray([65, 66, 67])
Z tekstu i kodowaniabytearray("ABC", "utf-8")
Z obiektu bytesbytearray(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.

OperacjaPython
Utworzeniebufor = bytearray(b"ABC")
Odczyt elementuprint(bufor[0])
Zmiana elementubufor[0] = 90
Dopisanie bajtubufor.append(68)
Rozszerzeniebufor.extend(b"EF")
Usunięciebufor.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ęzykPrzykład
Pythonbufor = bytearray(b"ABC")
bufor[1] = 88
print(bufor)
Cunsigned 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ładPython
Odczyt plikuwith open("plik.bin", "rb") as f:
  dane = bytearray(f.read())
Modyfikacjadane[0] = 255
Zapiswith 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ładPython
Bufor sieciowybufor = 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.

PoleceniePython
Ramkaramka = 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:

TypZachowanie
byteszmiana = nowy obiekt
bytearrayzmiana = 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.

SystemWartość liczby 65
Dziesiętny65
Binarny01000001
Szesnastkowy0x41

Zakres jednego bajtu wynika z prostego wzoru:

WzórZnaczenie
2^8 = 256liczba możliwych wartości
0 ... 255realny 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łądSkutek
bufor[0] = 300ValueError
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.

OperacjaPython
Konwersjawynik = 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.

OperacjaPython
Fragment buforafragment = 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

Dodaj komentarz