Pakiety

edytuj

W rzeczywistości przetwarzanie dokumentu XML jest bardzo proste, wystarczy jedna linia kodu. Jednakże, zanim dojdziemy do tej linii kodu, będziemy musieli krótko omówić, czym są pakiety.

Przykład. Ładowanie dokumentu XML
>>> from xml.dom import minidom    #(1)
>>> xmldoc = minidom.parse('~/diveintopython/common/py/kgp/binary.xml')
  1. Tej składni jeszcze nie widzieliśmy. Wygląda to niemal, jak from module import, który znamy i kochamy, ale z "." wygląda na coś wyższego i innego niż proste import. Tak na prawdę xml jest czymś, co jest znane pod nazwą pakiet (ang. package), dom jest zagnieżdżonym pakietem wewnątrz xml-a, a minidom jest modułem znajdującym się wewnątrz xml.dom.

Brzmi to skomplikowanie, ale tak naprawdę nie jest. Jeśli spojrzymy na konkretną implementację, może nam to pomóc. Pakiet to niewiele więcej niż katalog z modułami, a zagnieżdżone pakiety są podkatalogami. Moduły wewnątrz pakietu (lub zagnieżdżonego pakietu) są nadal zwykłymi plikami .py z wyjątkiem tego, że są w podkatalogu, zamiast w głównym katalogu lib/ instalacji Pythona.

Przykład. Plikowa struktura pakietu

Python21/ katalog główny instalacji Pythona (katalog domowy plików wykonywalnych) | +−−lib/ katalog bibliotek (katalog domowy standardowych modułów) | +−− xml/ pakiet xml (w rzeczywistości katalog z innymi rzeczami wewnątrz niego) | +−−sax/ pakiet xml.sax (ponownie, po prostu katalog) | +−−dom/ pakiet xml.dom (zawiera minidom.py) | +−−parsers/ pakiet xml.parsers (używany wewnętrznie)

Dlatego kiedy powiesz from xml.dom import minidom, Python zrozumie to jako "znajdź w katalogu xml katalog dom, a następnie szukaj tutaj modułu minidom i zaimportuj go jako minidom". Lecz Python jest nawet mądrzejszy; nie tylko możemy zaimportować cały moduł zawarty wewnątrz pakietu, ale także możemy wybiórczo zaimportować wybrane klasy czy funkcje z modułu znajdującego się wewnątrz pakietu. Możemy także zaimportować sam pakiet jako moduł. Składnia będzie taka sama; Python wywnioskuje, co masz na myśli na podstawie struktury plików pakietu i automatycznie wykona poprawną czynność.

Przykład. Pakiety także są modułami
>>> from xml.dom import minidom          #(1)
>>> minidom
<module 'xml.dom.minidom' from 'C:\Python21\lib\xml\dom\minidom.pyc'>
>>> minidom.Element
<class xml.dom.minidom.Element at 01095744>
>>> from xml.dom.minidom import Element  #(2)
>>> Element
<class xml.dom.minidom.Element at 01095744>
>>> minidom.Element
<class xml.dom.minidom.Element at 01095744>
>>> from xml import dom                  #(3)
>>> dom
<module 'xml.dom' from 'C:\Python21\lib\xml\dom\__init__.pyc'>
>>> import xml                           #(4)
>>> xml
<module 'xml' from 'C:\Python21\lib\xml\__init__.pyc'>
  1. W tym miejscu importujemy moduł (minidom) z zagnieżdżonego pakietu (xml.dom). W wyniku tego minidom został zaimportowany do naszej przestrzeni nazw. Aby się odwołać do klasy wewnątrz tego modułu (np. Element), będziemy musieli nazwę klasy poprzedzić nazwą modułu.
  2. Tutaj importujemy klasę (Element) z modułu (minidom), a ten moduł z zagnieżdżonego pakietu (xml.dom). W wyniku tego Element został zaimportowany bezpośrednio do naszej przestrzeni nazw. Dodajmy, że nie koliduje to z poprzednim importem; teraz do klasy Element możemy się odwoływać na dwa sposoby (lecz nadal jest to ta sama klasa).
  3. W tym miejscu importujemy pakiet dom (zagnieżdżony pakiet xml-a) jako sam w sobie moduł. Dowolny poziom pakietu może być traktowany jako moduł, co zresztą zobaczymy za moment. Może nawet mieć swoje własne atrybuty i metody, tak jak moduły, które widzieliśmy wcześniej.
  4. Tutaj importujemy jako moduł główny poziom pakietu xml.

Więc jak może pakiet (który na dysku jest katalogiem) zostać zaimportowany i traktowany jako moduł (który jest zawsze plikiem na dysku)? Odpowiedzią jest magiczny plik __init__.py. Wiemy, że pakiety nie są po prostu katalogami, ale są one katalogami ze specyficznym plikiem wewnątrz, __init__.py. Plik ten definiuje atrybuty i metody tego pakietu. Na przykład xml.dom posiada klasę Node, która jest zdefiniowana w xml/dom/__init__.py. Kiedy importujemy pakiet jako moduł (np. dom z xml-a), to tak naprawdę importujemy jego plik __init__.py.

Więc dlaczego męczyć się z pakietami? Umożliwiają one logiczne pogrupowanie powiązanych ze sobą modułów. Zamiast stworzenia pakietu xml z wewnętrznymi pakietami sax i dom, autorzy mogliby umieścić całą funkcjonalność sax w xmlsax.py, a całą funkcjonalność dom w xmldom.py, czy też nawet zamieścić wszystko w pojedynczym module. Jednak byłoby to niewygodne (podczas pisania tego podręcznika pakiet xml posiadał prawie 6000 linii kodu) i trudne w zarządzaniu (dzięki oddzielnym plikom źródłowym, wiele osób może równocześnie pracować nad różnymi częściami).

Jeśli kiedykolwiek będziemy planowali napisać wielki podsystem w Pythonie (lub co bardziej prawdopodobne, kiedy zauważymy, że nasz mały podsystem rozrósł się do dużego), zainwestujmy trochę czasu w zaprojektowanie dobrej architektury systemu pakietów. Jest to jedna z wielu rzeczy w Pythonie, w których jest dobry, więc skorzystajmy z tej zalety.