Zanurkuj w Pythonie/Pakiety
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.
>>> from xml.dom import minidom #(1) >>> xmldoc = minidom.parse('~/diveintopython/common/py/kgp/binary.xml')
- 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ż prosteimport
. Tak na prawdęxml
jest czymś, co jest znane pod nazwą pakiet (ang. package),dom
jest zagnieżdżonym pakietem wewnątrzxml
-a, aminidom
jest modułem znajdującym się wewnątrzxml.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.
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ść.
>>> 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'>
- W tym miejscu importujemy moduł (
minidom
) z zagnieżdżonego pakietu (xml.dom
). W wyniku tegominidom
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. - Tutaj importujemy klasę (
Element
) z modułu (minidom
), a ten moduł z zagnieżdżonego pakietu (xml.dom
). W wyniku tegoElement
został zaimportowany bezpośrednio do naszej przestrzeni nazw. Dodajmy, że nie koliduje to z poprzednim importem; teraz do klasyElement
możemy się odwoływać na dwa sposoby (lecz nadal jest to ta sama klasa). - W tym miejscu importujemy pakiet
dom
(zagnieżdżony pakietxml
-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. - 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.
Pakiet jest katalogiem ze specjalnym plikiem __init__.py wewnątrz niego. Plik __init__.py definiuje atrybuty i metody pakietu. Nie musi on definiować niczego, może być nawet pustym plikiem, lecz musi istnieć. Jeśli nie istnieje plik __init__.py, katalog jest tylko katalogiem, a nie pakietem, więc nie może być zaimportowany lub zawierać modułów, czy też zagnieżdżonych pakietów. |
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.