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

Usunięta treść Dodana treść
Piotr (dyskusja | edycje)
poprawki
Derbeth (dyskusja | edycje)
m deamerykanizacja pisowni
Linia 1:
== Abstrakcyjne źródła wejścia ==
 
Jedną z najważniejszych możliwości Pythona jest jego dynamiczne wiązanie, a jednym z najbardziej przydatnych przykładów wykorzystania tego jest ''obiekt pliko-podobnyplikopodobny'' (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-podobnyplikopodobny''.
 
W najprostszym przypadku ''obiekt pliko-podobnyplikopodobny'' jest dowolnym obiektem z metodą <code>read</code>, która przyjmuje opcjonalny parametr wielkości, <code>size</code>, a następnie zwraca łańcuch znaków. Kiedy wywołujemy go bez parametru <code>size</code>, odczytuje wszystko, 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ę <code>read</code> z parametrem <code>size</code>, to odczyta ona tyle bajtów ze źródła wejścia, ile wynosi wartość <code>size</code>, 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 przekazujemy do funkcji ''obiekt pliko-podobnyplikopodobny'', a funkcja ta po prostu wywołuje metodę <code>read</code>, 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ę zastanawiamy, co ma to wspólnego z przetwarzaniem XML-a? Otóż <code>minidom.parse</code> jest taką funkcją, do której możemy przekazać ''obiekt pliko-podobnyplikopodobny''.
 
{{Python/Przykład
Linia 70:
[...ciach...]</nowiki>}}
 
# Jak już zaobserwowaliśmy w poprzednim rozdziale, <code>urlopen</code> przyjmuje adres URL strony internetowej i zwraca ''obiekt pliko-podobnyplikopodobny''. Ponadto, co jest bardzo ważne, obiekt ten posiada metodę <code>read</code>, która zwraca źródło danej strony internetowej.
# Teraz przekazujemy ten ''obiekt pliko-podobnyplikopodobny'' do <code>minidom.parse</code>, która posłusznie wywołuje metodę <code>read</code> i parsuje dane XML, które zostają zwrócone przez <code>read</code>. Fakt, że te dane przychodzą teraz bezpośrednio z Internetu, jest kompletnie nieistotny. <code>minidom.parse</code> nie ma o stronach internetowych żadnego pojęcia; on tylko wie coś o ''obiektach pliko-podobnychplikopodobnych''.
# Jak tylko ''obiekt pliko-podobnyplikopodobny'', który podarował nam <code>urlopen</code>, 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].
}}
Linia 91:
OK, to możemy korzystać z funkcji <code>minidom.parse</code> 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 chcielibyśmy, aby nasz program mógł dać wyjście z pliku, adresu URL lub łańcucha znaków, potrzebujemy specjalnej logiki, aby sprawdzić czy mamy do czynienia z łańcuchem znaków, a jeśli tak, to wywołać funkcję <code>parseString</code> zamiast <code>parse</code>. Jakie to niesatysfakcjonujące...
 
Gdyby tylko był sposób, aby zmienić łańcuch znaków na obiekt ''pliko-podobnyplikopodobny'', to moglibyśmy po prostu przekazać ten obiekt do <code>minidom.parse</code>. I rzeczywiście, istnieje moduł specjalnie zaprojektowany do tego: <code>StringIO</code>.
 
{{Python/Przykład
Linia 112:
>>> ssock.close() #(6)
 
# Moduł <code>StringIO</code> zawiera tylko jedną klasę, także nazwaną <code>StringIO</code>, która pozwala zamienić napis w ''obiekt pliko-podobnyplikopodobny''. Klasa <code>StringIO</code> podczas tworzenia instancji przyjmuje jako parametr łańcuch znaków.
# Teraz już mamy ''obiekt pliko-podobnyplikopodobny'' i możemy robić wszystkie możliwe ''pliko-podobneplikopodobne'' operacje. Na przykład <code>read</code>, która zwraca oryginalny łańcuch.
# Wywołując ponownie <code>read</code> 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 <code>StringIO</code> pracuje w ten sam sposób.
# Możemy jawnie przesunąć się do początku napisu, podobnie jak możemy się przesunąć w pliku, wykorzystując metodę <code>seek</code> obiektu klasy <code>StringIO</code>.
Linia 121:
 
{{Python/Przykład
|10.5|Parsowanie XML-a z łańcucha znaków (sposób z obiektem pliko-podobnymplikopodobnym)
|tekst=
>>> contents = "<nowiki><grammar><ref id='bit'><p>0</p><p>1</p></ref></grammar></nowiki>"
Linia 131:
{{samp|<nowiki><grammar><ref id="bit"><p>0</p><p>1</p></ref></grammar></nowiki>}}
 
# Teraz możemy przekazać obiekt pliko-podobnyplikopodobny (w rzeczywistości instancję <code>StringIO</code>) do funkcji <code>minidom.parse</code>, która z kolei wywoła metodę <code>read</code> z tego ''obiektu pliko-podobnegoplikopodobnego'' 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ą pojedynczej funkcji, <code>minidom.parse</code>, sparsować dokument XML przechowywany na stronie internetowej, lokalnym pliku, czy w łańcuchu znaków. Dla strony internetowej wykorzystamy <code>urlopen</code>, aby dostać ''obiekt pliko-podobnyplikopodobny''; dla lokalnego pliku, wykorzystamy <code>open</code>; a w przypadku łańcucha znaków skorzystamy z <code>StringIO</code>. Lecz teraz pójdźmy trochę do przodu i uogólnijmy też te różnice.
 
{{Python/Przykład
Linia 159:
</pre>
 
# Funkcja <code>openAnything</code> przyjmuje pojedynczy argument, <code>source</code>, i zwraca ''obiekt pliko-podobnyplikopodobny''. <code>source</code> jest łańcuchem znaków o różnym charakterze. Może się odnosić do adresu URL (np. <code><nowiki>'http://slashdot.org/slashdot.rdf'</nowiki></code>), może być globalną lub lokalną ścieżką do pliku (np. <code>'binary.xml'</code>), czy też łańcuchem znaków przechowującym dokument XML, który ma zostać sparsowany.
# Najpierw sprawdzamy, czy <code>source</code> 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 <code>urllib</code> 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 <code>urllib</code> jest w stanie otworzyć <code>source</code>, to <code>return</code> wykopie nas bezpośrednio z funkcji i poniższe instrukcje <code>try</code> nie będą nigdy wykonywane.
# W innym przypadku, gdy <code>urllib</code> wrzasnął na nas i powiedział, że <code>source</code> nie jest poprawnym URL-em, zakładamy, że jest to ścieżka do pliku znajdującego się na dysku i próbujemy go otworzyć. Ponownie, nic nie robimy, by sprawdzić, czy <code>source</code> jest poprawną nazwą pliku (zasady określające poprawność nazwy pliku są znacząco 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 <code>source</code> jest łańcuchem znaków, który przechowuje dokument XML (ponieważ nic innego nie zadziałało), dlatego wykorzystujemy <code>StringIO</code>, aby utworzyć ''obiekt pliko-podobnyplikopodobny'' i zwracamy go. (Tak naprawdę, ponieważ wykorzystujemy funkcję <code>str</code>, <code>source</code> nie musi być nawet łańcuchem znaków; może być nawet dowolnym obiektem, a z którego zostanie wykorzystana jego tekstowa reprezentacja, a która jest zdefiniowana przez specjalną metodę <code>__str__</code>.)
}}
 
Linia 180:
}}
 
<noinclude>{{Nawigacja|Zanurkuj w Pythonie|
{{Nawigacja|Zanurkuj w Pythonie|
[[../Przetwarzanie XML-a - podsumowanie|Podsumowanie]]|
[[../Standardowy strumień wejścia, wyjścia i błędów/]]|
}}
{{Podświetl|py}}</noinclude>
</noinclude>