Zanurkuj w Pythonie/Abstrakcyjne źródła wejścia: Różnice pomiędzy wersjami

Usunięta treść Dodana treść
Piotr (dyskusja | edycje)
ok, przetłumaczone
Robwolfe (dyskusja | edycje)
Nie podano opisu zmian
Linia 2:
== Abstrakcyjne źródła wejścia ==
 
Jedną z najważniejszych możliwości Pythona jest jego dynamiczne łączeniewiązanie, a jednym z najbardziej przydatnych przykładów wykorzystania tego jest ''obiekt pliko-podobny'' (ang. ''file-like object'').
 
Wiele funkcji, które wymagają jakiegoś źródła wejścia, mogłyby po prostu przyjmować jako argument nazwę pliku, następnie go otwierać, czytać, a na końcu go zamykać. Jednak tego nie robią. Zamiast działać w ten sposób, jako argument przyjmują obiekt pliku lub ''obiekt pliko-podobny''.
 
W najprostszym przypadku ''obiekt pliko-podobny'' jest dowolnym obiektem z metodą <tt>read</tt>, która przyjmuje opcjonalny parametr wielkości <tt>size</tt>, a następnie zwraca łańcuch znaków. Kiedy wywołujemy go bez parametru <tt>size</tt>, odczytuje wszytkowszystko, co jest do przeczytania ze źródła wejścia, a potem zwraca te wszystkie dane jako pojedynczy łańcuch znaków. Natomiast kiedy wywołamy metodę <tt>read</tt> z parametrem <tt>size</tt>, to odczyta ona tyle bajtów ze źródła wejścia, ile wynosi wartość <tt>size</tt>, a następnie zwróci te dane. Kiedy ponownie wywołamy tę metodę, zostanie odczytana i zwrócona dalsza porcja danych (czyli dane będą czytane od miejsca, w którym wcześniej skończono czytać).
 
Powyżej opisaliśmy, w jaki sposób działają prawdziwe pliki. Jednak nie musimy się ograniczać do prawdziwych plików. Źródłem wejścia może być wszystko: plik na dysku, strona internetowa, czy nawet jakiś łańcuch znaków. Dopóki przekazujesz do funkcji ''obiekt pliko-podobny'', a funkcja ta po prostu wywołuje metodę <tt>read</tt>, to funkcja może obsłużyć dowolny rodzaj wejścia, bez posiadania jakiegoś specjalnego kodu dla każdego rodzaju wejścia.
 
Może się zastanawiasz, co ma to wspólnego z przetwarzaniem XML-a? Otóż <tt>minidom.parse</tt> jest taką funkcjifunkcją, do której możemy przekazać ''obiekt pliko-podobny''.
 
'''Przykład 10.1. Parsowanie XML-u z pliku'''
Linia 34:
 
# Najpierw otwieramy plik z dysku. Otrzymujemy przez to obiekt pliku.
# Przekazujemy obiekt pliku do funkcji <tt>minidom.parse</tt>, a któryktóra wywołuje metodę <tt>read</tt> z <tt>fsock</tt> i czyta dokument XML z tego pliku.
# Koniecznie wywołujemy metodę <tt>close</tt> z obiektu pliku, jak już skończyliśmy na nim pracę. <tt>minidom.parse</tt> nie zrobi tego za nas.
# Wywołując ze zwróconego dokumentu XML metodę <tt>toxml()</tt>, wypisujemy cały dokument.
 
Dobrze, to wszystko wygląda jak kolosalne marnotrawstwo czasu. MimoW wszystko,końcu już wcześniej zobaczyliśmy jużwidzieliśmy, że <tt>minidom.parse</tt> może przyjąć jako argument nazwę pliku, któryi wykonać całą robotę potemz otwieraotwieraniem i zamykazamykaniem automatycznie. ToPrawdą prawdąjest, że jeśli chcesz sparsować lokalny plik, możesz przekazać nazwę pliku do <tt>minidom.parse</tt>, a funkcja ta będzie umiała mądrze to wykorzystać. Lecz zauważmy, żejak będziepodobne takżei łatwołatwe jest także parsowaćparsowanie dokumentydokumentu XML pochodzącepochodzącego bezpośrednio z Internetu.
 
'''Przykład 10.2. Parsowanie XML-a z URL-a '''
Linia 73:
 
# Jak już zaobserwowaliśmy w poprzednim rozdziale, <tt>urlopen</tt> przyjmuje adres URL strony internetowej i zwraca ''obiekt pliko-podobny''. Ponadto, co jest bardzo ważne, obiekt ten posiada metodę <tt>read</tt>, która zwraca źródło danej strony internetowej.
# Teraz przekazujemy ten ''obiekt pliko-podobny'' do <tt>minidom.parse</tt>, któryktóra posłusznie wywołuje metodę <tt>read</tt> i parsuje dane XML, które zostają zwrócone przez <tt>read</tt>. Fakt, że te dane przychodzą teraz bezpośrednio z Internetu, jest kompletnie nie związany z tematemnieistotny. <tt>minidom.parse</tt> nie ma o stronach internetowych żadnego pojęcia; on tylko wie coś o ''obiektach pliko-podobnych''.
# Jak tylko ''obiekt pliko-podobny'', który podarował nam <tt>urlopen</tt>, nie będzie potrzebny, koniecznie zamykamy go.
# Przy okazji, ten URL jest prawdziwy i on naprawdę jest dokumentem XML. Reprezentuje on aktualne nagłówki, techniczne newsy i plotki w [http://slashdot.org/ Slashdot].
 
'''Przykład 10.3. PrzetwarzaniaParsowanie XML-a z łańcucha znaków (prosty sposób, ale mało elastyczny)'''
 
<nowiki>
Linia 91:
OK, to możemy korzystać z funkcji <tt>minidom.parse</tt> zarówno do parsowania lokalnych plików jak i odległych URL-ów, ale do parsowania łańcuchów znaków wykorzystujemy... inną funkcję. Oznacza to, że jeśli chciałbyś móc dać wyjście z pliku, adresu URL lub łańcucha znaków, potrzebujesz specjalnej logiki, aby sprawdzić czy mamy do czynienia z łańcuchem znaków, a jeśli tak, to wywołać funkcję <tt>parseString</tt> zamiast <tt>parse</tt>. Jakie to niesatysfakcjonujące.
 
JeśliGdyby mamytylko był sposób, aby zmienić łańcuch znaków na obiekt ''pliko-podobny'', to możemymoglibyśmy po prostu przekazać ten obiekt do <tt>minidom.parse</tt>. I rzeczywiście, istnieje moduł specjalnie zaprojektowany do tego: StringIO.
 
'''Przykład 10.4. Wprowadzenie do StringIO'''
Linia 113:
 
# Moduł <tt>StringIO</tt> zawiera tylko jedną klasę, także nazwaną <tt>StringIO</tt>, która pozwala zamienić napis w ''obiekt pliko-podobny''. Klasa <tt>StringIO</tt>, podczas tworzenia instancji, przyjmuje jako parametr łańcuch znaków.
# Teraz już mamy ''obiekt pliko-podobny'' i możemy robić wszystkie możliwe ''pliko-podobne'' operacje. Na przykład <tt>read</tt>, któryktóra zwraca oryginalny łańcuch.
# Wywołując ponownie <tt>read</tt> otrzymamy pusty napis. W ten sposób działa prawdziwy obiekt pliku; kiedy już zostanie przeczytany cały plik, nie można czytać więcej bez wyraźnego przesunięcia do początku pliku. Obiekt <tt>StringIO</tt> pracuje w ten sam sposób.
# Możemy wyraźniejawnie przesunąć się do początku napisu, podobnie jak możemy się przesunąć przezw plikpliku, wykorzystując metodę <tt>seek</tt> obiektyobiektu <tt>StringIO</tt>.
# Możemy także czytać fragmentami łańcuch znaków, przekazując parametr wielkości <tt>size</tt> do metody <tt>read</tt>.
# Za każdym razem, kiedy wywołamy <tt>read</tt>, zostanie nam zwrócona pozostała część napisu, która nie została jeszcze przeczytana. W dokładnie ten sam sposób działa obiekt pliku.
Linia 132:
# Teraz możemy przekazać obiekt pliko-podobny (w rzeczywistości <tt>StringIO</tt>) do <tt>minidom.parse</tt>, który wywoła metodę <tt>read</tt> z tego obiektu i szczęśliwie wszystko przeparsuje, nie zdając sobie nawet sprawy, że wejście to pochodzi z łańcucha znaków.
 
To już wiemy, jak za pomocą pojedyńczejpojedynczej funkcji, <tt>minidom.parse</tt>, sparsować dokument XML przechowywany na stronie internetowej, lokalnym pliku, czy w łańcuchu znaków. Dla strony internetowej wykorzystamy <tt>urlopen</tt>, aby dostać ''obiekt pliko-podobny''; dla lokalnego pliku, wykorzystamy <tt>open</tt>; a w przypadku łańcucha znaków skorzystamy z <tt>StringIO</tt>. Lecz teraz pójdźmy trochę do przodu i uogólnijmy też te różnice.
 
'''Przykład 10.6. <tt>openAnything</tt>'''
Linia 159:
# Najpierw sprawdzamy, czy <tt>source</tt> jest URL-em. Robimy to brutalnie: próbujemy otworzyć to jako URL i cicho pomijamy błędy spowodowane próbą otworzenia czegoś, co nie jest URL-em. Jest to właściwie eleganckie w tym sensie, że jeśli <tt>urllib</tt> będzie kiedyś obsługiwał nowe typy URL-i, nasz program także je obsłuży i to bez konieczności zmiany kodu. Jeśli <tt>urllib</tt> jest w stanie otworzyć <tt>source</tt>, to <tt>return</tt> wykopie nas bezpośrednio z funkcji i poniższe instrukcje <tt>try</tt> nie będą nigdy wykonywane.
# W innym przypadku, gdy <tt>urllib</tt> wrzasnął na nas i powiedział, że <tt>source</tt> nie jest poprawnym URL-em, zakładamy, że jest to ścieżka do pliku znajdującym się na dysku i próbujemy go otworzyć. Ponownie, nic nie robimy, by sprawdzić, czy <tt>source</tt> jest poprawną nazwą pliku (zasady określające poprawność nazwy pliku są szalenie różne na różnych platformach, dlatego prawdopodobnie i tak byśmy to źle zrobili). Zamiast tego, na ślepo otwieramy plik i cicho pomijamy wszystkie błędy.
# W tym miejscu zakładamy, że <tt>source</tt> jest łańcuchem znaków, który przechowuje dokument XML (ponieważ nic innego nie zadziałało), dlatego wykorzystujemy <tt>StringIO</tt>, aby utworzyć ''obiekt pliko-podobny'' i zwracamy go. (Tak naprawdę, ponieważ wykorzystujemy funkcjefunkcję <tt>str</tt>, <tt>source</tt> nie musi być nawet łańcuchem znaków; może być nawet dowolnym obiektem, a z którego zostanie wykorzystana jego tekstowątekstowa <ref>łańcuchowo-znakowa</ref> reprezentacja, a która jest zdefiniowana przez specjalną metodę <tt>__str__</tt>.)
 
Teraz możemy wykorzystać funkcjefunkcję <tt>openAnything</tt> w połączeniu z <tt>minidom.parse</tt>, aby utworzyć funkcjefunkcję, która przyjmuje źródło <tt>source</tt>, które w jakiś sposób odwołuje się do dokumentu XML (może to robić za pomocą adresu URL, lokalnego pliku, czy też dokumentu przechowywanego jako łańcuch znaków), i parsuje je.
 
'''Przykład 10.7. Wykorzystanie <tt>openAnything</tt> w kgp.py'''