Zaawansowany kurs programowania w Pythonie
Rozdział
>
Poziom
Moduły serializacji
Moduł struktury
Cel
Ustal ostateczny podział danych dla nowego gospodarstwa rolnego przy użyciu modułu struct.
Na końcu drogi znajduje się stacja obsługi, która administrowała nowym gospodarstwem rolnym, które już jest zbudowane, a uprawy zostały już posadzone. Tutaj będziemy analizować i przetwarzać dane dotyczące już posadzonych upraw oraz prognozowanego wydobycia z gospodarstwa. Podobnie jak na innych poziomach tego rozdziału, będziemy pracować z serializacją i deserializacją danych, wprowadzając ostatni moduł o nazwie struct.
Moduł struct wprowadza szereg funkcji do serializacji, które pakują dane w formacie binarnym. W przeciwieństwie do innych modułów, z którymi pracowaliśmy, masz większą kontrolę nad sposobem strukturyzacji danych zarówno podczas ich serializacji, jak i późniejszej deserializacji, co czyni go bardziej wszechstronnym. Użyj import struct, aby uzyskać dostęp do następujących funkcji, których będziemy używać do przetwarzania danych:
struct.calcsize(): Określa, ile bajtów zajmuje dany ciąg formatu. Przyjmuje jeden (1) argument, którym jest format, dla którego chcesz sprawdzić rozmiar w bajtach. Formatami, których będziemy używać, są:- integer: reprezentowany jako
'i', jest formatem dla danych reprezentowanych jako liczby całkowite - float: reprezentowany jako
'f', jest formatem dla danych reprezentowanych jako liczby dziesiętne - double: reprezentowany jako
'd', jest formatem dla danych reprezentowanych jako bardziej precyzyjne liczby dziesiętne, gdy format float jest niewystarczający.
- integer: reprezentowany jako
struct.pack(): Serializuje dane w formacie binarnym, pakując je w wybranym formacie. Może przyjmować dwa (2) lub więcej argumentów, gdzie pierwszy argument to format, którego chcesz użyć, a kolejne to wartości, które chcesz zserializować. Formatów używamy tych, które zostały wcześniej opisane, i musisz podać je odpowiadająco do liczby argumentów.struct.unpack(): Deserializuje zapakowane dane binarne. Przyjmuje dwa (2) argumenty: format, który musi być zgodny z formatem użytym przy serializacji, oraz dane, które zostały zserializowane.struct.iter_unpack(): Deserializuje zapakowane dane binarne. Działa tak samo jakstruct.unpack(), ale iteruje przez każdy blok danych osobno za pomocą pętli.struct.pack_into(): Zaawansowana wersjastruct.pack(), przyjmuje cztery (4) argumenty: format, którego chcesz użyć, bufor danych, do którego chcesz wstawić dane, pozycję w buforze, którą dane mają zająć, oraz same dane do zapakowania.struct.unpack_from(): Zaawansowana wersjastruct.unpack(), przyjmuje trzy (3) argumenty: format, którego chcesz użyć, bufor danych, z którego chcesz deserializować, oraz pozycję w buforze, od której chcesz rozpocząć deserializację. Pozwala to na deserializację określonych fragmentów danych.
Przejdź do znaku X w świetle na stacji obsługi i zwróć się do biurka, utwórz trzy (3) zmienne o nazwach: integer, float i double. Użyjemy ich do sprawdzenia rozmiaru w bajtach każdego formatu przy użyciu funkcji struct.calcsize(). Dla zmiennej integer użyj funkcji z argumentem 'i', dla zmiennej float z argumentem 'f', a dla zmiennej double z argumentem 'd'. Na przykład: integer = struct.calcsize('i'). Dodaj te trzy (3) zmienne do predefiniowanej funkcji write().
Przejdź do znaku Złota X i użyj funkcji read(), aby zebrać dane dotyczące nowego gospodarstwa rolnego. Zanotuj punkty danych i format do przyszłego wykorzystania, mianowicie: Resources, Size i Estimate. Gdy to zanotujesz, przejdź do znaku X w świetle na niebieskim dywanie i utwórz zmienną o nazwie blue_data.
W zmiennej blue_data przechowuj wartość funkcji struct.pack(), ustawiając jako argumenty format oraz wcześniej zanotowane wartości. Przy zapisywaniu formatu musisz "spakować" typy formatów w jedną całość. Na przykład, format integer oznaczony jest jako 'i', jak wspomniano wcześniej; jeśli dodajesz trzy liczby całkowite, zapiszesz je jako 'iii'. Analogicznie, jeśli dodajesz integer, float i double, zapiszesz to jako 'ifd'. Dodając dane, umieszczaj je jako oddzielne argumenty. Oto przykład:
data_1 = 8 # is an integer data_2 = 2.25 # is a float data_3 = 900.702938103 # is a double blue_data = struct.pack('ifd', data_1, data_2, data_3)
Funkcja struct.pack() oferuje dużą elastyczność, pozwalając określić sposób formatowania danych oraz umożliwiając serializację wielu punktów danych jednocześnie, według Twojego uznania. Dodaj wcześniej odczytane dane, używając powyższego przykładu jako podstawy. Użyj funkcji display(), przekazując blue_data jako argument, aby wyświetlić zapakowane dane.
Przejdź do znaku ciemnego X na niebieskim dywanie i zwróć się do terminala, utwórz zmienną o nazwie blue_unpack i zapisz w niej wartość funkcji struct.unpack(), przekazując jako argumenty ten sam format użyty do pakowania danych oraz zmienną blue_data. Format zapisujesz w ten sam sposób, jak w funkcji struct.pack(), dzięki czemu możesz deserializować dane w taki sam sposób, w jaki je pierwotnie serializowałeś. Użyj funkcji write(), przekazując blue_unpack jako argument, aby potwierdzić dane, które wcześniej zapakowałeś.
W tym samym miejscu użyjemy także funkcji struct.iter_unpack(), która przyjmuje dokładnie te same argumenty co struct.unpack(), jednak teraz jest używana w pętli for, co pozwala nam iterować przez dane zamiast wypisywać je wszystkie na raz. Funkcja jest napisana następująco:
for values in struct.iter_unpack(-insert value-, -insert value-): player.speak(values)
Będziemy używać funkcji speak(), aby wyświetlić wartości. Dla brakujących argumentów w funkcji struct.iter_unpack() dodaj te same, które wcześniej używałeś z funkcją struct.unpack(), ponieważ są to warianty tej samej funkcji pod względem użycia.
Przejdź do znaku Złota X na czerwonym dywanie i zwróć się do biurka, użyj funkcji read(), aby przejrzeć ilości upraw. Zanotuj wszystkie ilości oraz format dla każdej uprawy. Przejdź do znaku X w świetle na czerwonym dywanie i utwórz obiekt o nazwie buffer, przypisując mu wartość bytearray(16). Jest to zbiór bajtów, do którego możemy się odwołać przed jego wypełnieniem — dla naszych celów działa jak bank danych, w którym możesz ręcznie przechowywać dane. Liczba 16 jako argument oznacza długość, czyli liczbę bajtów, które możesz przechowywać w obiekcie buffer.
Użyj funkcji struct.pack_into() do wypełnienia utworzonego obiektu buffer. Nie musisz tworzyć zmiennej do zapisywania wartości funkcji, ponieważ sama funkcja wstawi wartości bezpośrednio do obiektu buffer. Będziemy używać tej funkcji dla każdej z wcześniej zanotowanych wartości upraw, przekazując odpowiednie argumenty.
Na przykład, dla pierwszej wartości uprawy, otrzymasz jej format, pozycję w bajtach oraz ilość. Dodaj format jako pierwszy argument, miejsce, do którego chcesz zapisać bajty (w tym przypadku buffer) jako drugi argument, a jako trzeci argument ustaw pozycję w buffer, w której chcesz zapisać wartość. Format określa liczbę zajmowanych bajtów, więc upewnij się, że pakujesz dane w miejsce, które nie jest już zajęte. Na końcu dodaj wartość, którą chcesz zserializować i zapakować do obiektu buffer.
struct.pack_into('i', buffer, 0, 82)
Obiekt buffer ma 16 bajtów, jak wspomniano wcześniej; w powyższym przykładzie format integer zajmuje 4 bajty i jest wstawiany na pozycji 0. Oznacza to, że po wprowadzeniu danych do buffer pozostaje tylko 12 wolnych bajtów, gdzie pozycje 0-3 są zajęte podaną wartością, w tym przypadku 82. Zrób to dla wszystkich punktów danych dotyczących upraw, których jest łącznie trzy (3). Użyj funkcji display() z argumentem buffer, aby wyświetlić zserializowane dane.
Przejdź do znaku ciemnego X na czerwonym dywanie i zwróć się do terminala; tutaj będziemy deserializować dane, aby potwierdzić ich zawartość i upewnić się, że zostały poprawnie zapisane. Utwórz trzy zmienne o nazwach: lettuce, carrots i melons. Zdeserializujemy dane dla każdego punktu danych osobno. Dla każdej zmiennej zapisz wartość funkcji struct.unpack_from() i przekaż jako argumenty te same dane, które zanotowałeś przy pakowaniu. Jako pierwszy argument ustaw format, jako drugi dodaj obiekt buffer, czyli miejsce, z którego odbierasz dane, a na końcu podaj pozycję w buffer, od której chcesz rozpocząć deserializację. Na przykład:
lettuce = struct.unpack_from('i', buffer, 0)
Te dane odpowiadają wcześniejszemu przykładowi pakowania, który teraz jest deserializowany. Zrób to samo dla pozostałych dwóch zmiennych i przekaż lettuce, carrots oraz melons do predefiniowanej funkcji write(), aby ukończyć poziom.