Zapisz jako PDF

Transkrypt

Zapisz jako PDF
Spis treści
1 XML
1.1 Chwila refleksji
2 Parsery
2.1 lxml.etree
2.1.1 import
2.1.2 Klasa Element
2.1.3 Elementy są sekwencjami
2.1.4 Elementy mają atrybuty
2.2 Atrybuty są słownikami
2.3 Pliki
2.3.1 Odczyt
2.3.2 Zapis
2.4 Przydatne funkcje
2.4.1 find / findall
2.5 Tworzenie własnego parsera
2.5.1 Ćwiczenie
2.6 Zadania
2.6.1 Wstęp do projektu zaliczeniowego: Temat 1 — tworzenie klas na podstawie
opisu w XML
2.6.1.1 Dynamiczna generacja klas
2.6.1.2 Treść projektu
2.6.2 Projekt zaliczeniowy: Temat 1 -- tworzenie klas na podstawie opisu w XML
2.6.3 FAQ do Tematu 1
2.6.4 Wstęp do projektu zaliczeniowego: Temat 2
2.6.5 Projekt zaliczeniowy: Temat 2 -- Parsowanie pliku z metaopisem danych
2.6.6 FAQ do Tematu 2
3 Literatura
XML
Nazwa XML jest skrótem od (ang. Extensible Markup Language), co w wolnym tłumaczeniu oznacza
"rozszerzalny język znaczników". Sam XML jest uniwersalnym językiem przeznaczonym do
reprezentowania różnych danych w strukturalizowany sposób.
Język formalny
język złożony ze wszystkich słów (wyrażeń) uzyskiwanych za pomocą ściśle określonych reguł;
definiowany zwykle przez gramatykę formalną (zgodnie ze składnią języka formalnego); podstawowe
pojęcie lingwistyki matematycznej (za Encyklopedią PWN ).
XML nie jest językiem programowania i nie trzeba być programistą, by z niego korzystać. XML
sprawia, że generowanie danych i ich odczyt są znacznie łatwiejsze, zapewniając strukturę łatwą do
generacji i odczytu programowego. XML jest rozszerzalny i niezależny od platformy, i wykorzystuje
Unicode do reprezentacji tekstu.
XML korzysta z tagów (słowa ujęte w '<' i '>') oraz atrybutów (w postaci nazwa="wartość"), bardzo
podobnie do HTMLa. Jednakowoż HTML definiuje dokładne znaczenia każdego z tagów i jego
atrybutów, jak również ich wygląd w przeglądarce, XML używa tagów tylko do rozgraniczenia
pewnej części z całego dokumentu, a interpretację znaczenia pozostawia aplikacji odczytujacej te
dane. I tak "<p>" w dokumencie XML-owym nie oznacza początku paragrafu. W zależności od
kontekstu, czy konstrukcji formatu danych taki tag może oznaczać dowolny parametr zaczynający się
na "p" albo na inną literę alfabetu.
Pliki XML-owe tak samo jako HTML-owe są plikami tekstowymi. Dzięki temu można przeglądać dane
bez potrzeby korzystania z dodatkowych aplikacji, a w ostateczności edytować je korzystając z
edytora tekstu. Postać tekstowa umożliwia też łatwiejsze przeglądanie tekstu w celu usuwania
błędów.
W przeciwieństwie do HTML, reguły dotyczące plików XML są ścisłe i nie naruszalne. Źle napisany
tag, zgubiony nawias czy atrybut nie ujęty w cudzysłów czyni plik XML bezużytecznym, podczas gdy
w HTML taka praktyka jest dozwolona. Oficjalna specyfikacja języka XML zabrania aplikacjom
domyślać się co ma znaczyć dany fragment uszkodzonego plik XML; jeśli w pliku jest błąd program
powinien wstrzymać wykonywanie i zgłosić błąd.
Ze względu na postać tekstową danych i rozgraniczanie ich za pomocą tekstowych znaczników, pliki
XML-owe są znacząco większe niż pliki biarne z takimi samymi danymi.
XML pozwala na zdefiniowanie nowego formatu poprzez łączenie lub korzystanie z
innego. Ponieważ różne formaty są tworzone zupełnie niezalenie, mogą mieć tagi lub
atrybuty o takiej samej nazwie, co powoduje dwuznaczność przy łączeniu takich
dokumentów (np. w jednym "<p>" oznacza "paragraf", a w innym "pisarza"). W celu
wyeliminowania takiej dwuznaczności w XML wprowadzona mechanizm przestrzeni
nazw.(...)XML Schema został zaprojektowany, by odzwierciedlać to wsparcie dla
modularności na poziomie definiowania struktury dokumentu XML, ułatwiając
połaczeniu dwóch schematów w celu stworzenia trzeciego, który obejmuje strukturę
połaczonych dokumentów.
(XML in 10 points, W3C Communications Team tłumaczenie Jacek Gleń)
Chwila refleksji
Zatrzymajmy się na chwilę, żeby się z oswoić z wyglądem pliku xml prosciutkiego i bardziej
skomplikowanego. Podziwiajmy przez chwilę drzewiastą strukturę tych plików, popatrzmy jakie są
zasadnicze różnice między nimi.
Parsery
lxml.etree
Moduł lxml jest pythonową nakładką na na biblioteki libxml2 i libxslt napisane w języku C. Jest o
tyle przyjemna, że łączy szybkość działania i kompletność z naturalnym dla nas interfejsem
pythonowym i jest kompatybilna z ElementTree.
Poniższe przykłady są częściowo zaczerpnięte z samouczka do lxml.etree autorstwa Stefana
Behnela.
import
Zazwyczaj piszemy:
from lxml import etree
W przypadku, gdy program nie będzie korzystał z funkcjonalności dostępnej wyłącznie w lxml.etree
import można zrobić w następujący sposób:
try:
from lxml import etree
print "running with lxml.etree"
except ImportError:
try:
# Python 2.5
import xml.etree.cElementTree as etree
print "running with cElementTree on Python 2.5+"
except ImportError:
try:
# Python 2.5
import xml.etree.ElementTree as etree
print "running with ElementTree on Python 2.5+"
except ImportError:
try:
# normal cElementTree install
import cElementTree as etree
print "running with cElementTree"
except ImportError:
try:
# normal ElementTree install
import elementtree.ElementTree as etree
print "running with ElementTree"
except ImportError:
print "failed to import ElementTree from any known place"
Klasa Element
Element jest podstawowym kontenerem w ElementTree. W większości wypadków do drzewa XMLowego dostajemy się przez tęże klasę.
Tworzymy obiekt o nazwie root klasy Element:
>>> root = etree.Element("root")
Do XML-owej nazwy znacznika (taga) dostajemy się za pomocą pola tag:
>>> print root.tag
root
Obiekty klasy Element tworzą strukturę drzewa XML-owego. Żeby skontruować dzieci i dodać je do
rodzica, można m.in. użyć metody append:
>>> root.append( etree.Element("child1") )
Można też do tego użyć fabryki SubElement, która wymaga podania nazwy rodzica jako parametru
oraz takich samych parametrów, jak fabryka Element.
>>> child2 = etree.SubElement(root, "child2")
>>> child3 = etree.SubElement(root, "child3")
Zobaczmy, jakie drzewo xml-owe stworzyliśmy:
>>> print(etree.tostring(root, pretty_print=True))
<root>
<child1/>
<child2/>
<child3/>
</root>
Elementy są sekwencjami
Żeby było jeszcze prościej, elementy zachowują się jak sekwencje:
>>> child = root[]
>>> print child.tag
child1
>>> print len(root)
3
>>> root.index(root[1]) # tylko w lxml.etree!
1
>>> children = list(root)
>>> for child in root:
...
print child.tag
child1
child2
child3
>>> root.insert(, etree.Element("child0"))
>>> start = root[:1]
>>> end
= root[-1:]
>>> print start[].tag
child0
>>> print end[].tag
child3
>>> root[] = root[-1] # zmienia kolejność elementów w drzewie w lxml.etree
>>> for child in root:
...
print child.tag
child3
child1
child2
Elementy mają atrybuty
W XML-u tagi mogą mieć atrybuty. Tagi z atrybutami, można tworzyć bezpośednio za pomocą klasy
Element:
>>> root = etree.Element("przedmiot", interesujacy="strasznie")
>>> etree.tostring(root)
b'<przedmiot interesujacy="strasznie"/>'
Dostęp do atrybutów zapewniany jest przez metody set i get:
>>> print root.get("interesting")
None
>>> print root.get("interesujacy")
strasznie
>>> root.set("interesujacy","troszke")
>>> print root.get("interesujacy")
troszke
Za pomocą metody set można tez atrybuty dodawać:
>>> root.set("trudny","troszke")
>>> print root.get("interesujacy")
troszke
Atrybuty można też uzyskać za pomocą metody attrib:
>>> print root.attrib
{'interesujacy': 'troszke', 'trudny': 'troszke'}
Atrybuty są słownikami
Kontynuując poprzedni przykład, attrib zwraca nam słownik atrybutów danego taga:
>>> child = root[]
>>> child.tag
'child1'
>>> child.set("atr", "1")
>>> print child
<Element child1 at 13943a0>
>>> child.attrib
{'atr': '1'}
>>> child.set("atr2", "2")
>>> child.attrib
{'atr2': '2', 'atr': '1'}
Pliki
Parsery xml jednak najczęściej służą do przetwarzania w jakiś sposób plików, a nie tylko tworzenia
struktury xml'owej w pamięci, więc teraz przyjrzymy się prostym przykładom plików:
Odczyt
<root>
<child>One</child>
<child>Two</child>
</root>
Skopiujmy gdzieś ten kawałek, i zapiszmy pod nazwą example.xml.
>>> xml_file = "/sciezka/bezwzgledna/example.xml"
>>> tree = ET.parse(xml_file)
>>> element = tree.getroot()
>>> print element.tag
root
>>> for subelement in element:
print subelement.text
One
Two
Żeby nie mieszało nam się, które słówka są kluczowe, a które możemy samodzielnie wymyślać,
przepiszmy ten przykład na język polski:
<korzen>
<dziecko>Pierwsze</dziecko>
<dziecko>Drugie</dziecko>
</korzen>
>>>
>>>
>>>
>>>
plik_xml = "/Users/magda/przyklad.xml"
drzewko = ET.parse(plik_xml)
el = drzewko.getroot()
for podelement in el:
print podelement.text
Pierwsze
Drugie
>>> print el.tag
korzen
Wzbogaćmy nasz przykładowy plik o atrybuty w tagach:
<korzen>
<dziecko atrybut="wartosc" atrybut2="wartosc2">Pierwsze</dziecko>
<dziecko>Drugie</dziecko>
</korzen>
from xml.etree import ElementTree as ET
plik_xml = "/Users/magda/przyklad.xml"
drzewko = ET.parse(plik_xml)
el = drzewko.getroot()
for podelement in el:
print podelement.text
print podelement.attrib
# Wynik wykonania:
>>>
Pierwsze
{'atrybut2': 'wartosc2', 'atrybut': 'wartosc'}
Drugie
{}
Zapis
Przydatne funkcje
API biblioteki lxml.etree oraz xml.etree są bardzo podobne. Na potrzeby naszych zajęć
prawdopodobnie nawet nie zauważymy różnicy. Wygodna dokumentacja jest tu(xml.etree) oraz tu
(lxml) Poniżej kilka przykładów.
find / findall
pozwalające wybrać pierwszy/wszystkie tagi o zadanej nazwie:
<korzen>
<dziecko atrybut="wartosc" atrybut2="wartosc2">Pierwsze</dziecko>
<dziecko>Drugie</dziecko>
<atrapa>Atrapa</atrapa>
<atrapa>Atrapa2</atrapa>
<atrapa>Atrapa3</atrapa>
</korzen>
from xml.etree import ElementTree as ET
plik_xml = "/Users/magda/przyklad.xml"
drzewko = ET.parse(plik_xml)
el = drzewko.getroot()
dzieci = el.findall("dziecko")
for d in dzieci:
print d.text
atrapy = el.findall("atrapa")
for a in atrapy:
print a.text
# Wynik wykonania:
>>>
Pierwsze
Drugie
Atrapa
Atrapa2
Atrapa3
A ile tagów o nazwie dziecko zostanie znalezione w tym przypadku?:
<korzen>
<dziecko atrybut="wartosc" atrybut2="wartosc2">Pierwsze</dziecko>
<dziecko>Drugie</dziecko>
<atrapa>Atrapa</atrapa>
<atrapa>Atrapa2</atrapa>
<atrapa>Atrapa3</atrapa>
<dziecko>
<dziecko>
<dziecko>
</dziecko>
</dziecko>
</dziecko>
</korzen>
from xml.etree import ElementTree as ET
plik_xml = "/Users/magda/przyklad.xml"
drzewko = ET.parse(plik_xml)
el = drzewko.getroot()
dzieci = el.findall("dziecko")
print len(dzieci)
# Wynika wykonania:
>>>
3
Tworzenie własnego parsera
Do budowania drzewa, na którym potem wykonujemy wszystkie operacje, jest używany standardowy
"parser". Ale czasem chcielibyśmy wykonać jakieś operacje już na etapie budowania drzewa. Mamy
taką możliwość, możemy utworzyć własną klasę, której przedefiniujemy odpowiednie metody, i
podać ją jako parametr odpowiednim funkcjom. W szczególności w powyższych przykładach
dotyczących plików mogliśmy funkcji parse jako drugi parametr podać nasz własny parser:
xml.etree.ElementTree.parse(source, parser=None)
Parses an XML section into an element tree. source is a filename or file
object
containing XML data. parser is an optional parser instance. If not
given, the standard XMLParser parser is used. Returns an ElementTree
instance.
A oto przykład tworzenia własnego parsera, który przy okazji parsowania zlicza głębokość drzewka:
>>>
>>>
...
...
...
...
...
...
...
...
...
...
...
...
...
>>>
>>>
>>>
...
...
...
...
...
...
...
...
...
...
>>>
>>>
4
from xml.etree.ElementTree import XMLParser
class MaxDepth:
# The target object of the parser
maxDepth =
depth =
def start(self, tag, attrib):
# Called for each opening tag.
self.depth += 1
if self.depth > self.maxDepth:
self.maxDepth = self.depth
def end(self, tag):
# Called for each closing tag.
self.depth -= 1
def data(self, data):
pass
# We do not need to do anything with data.
def close(self):
# Called when all data has been parsed.
return self.maxDepth
target = MaxDepth()
parser = XMLParser(target=target)
exampleXml = """
<a>
<b>
</b>
<b>
<c>
<d>
</d>
</c>
</b>
</a>"""
parser.feed(exampleXml)
parser.close()
Pamiętaj, że gdy używamy argumentu target, XMLParser() nie zwraca automatycznie drzewa -- jesli
chcemy, musimy sami je zbudować w naszej klasie, którą podajemy jako target.
Ćwiczenie
Napisz parser, który podczas budowania drzewa zlicza ilość elementów o nazwie child Przetestuj
jego działanie.
Zadania
Termin oddania projektów do 15-tego czerwca!
Zmiany w terści zadań, wynikające z uwag studentów, akceptowane są do 1-go
czerwca!
Wstęp do projektu zaliczeniowego: Temat 1 — tworzenie klas na podstawie opisu w XML
Dynamiczna generacja klas
Python umożliwia dynamiczną generację klas.
Można to robić na dwa sposoby. W funkcji:
def klasa_p(p):
class P(object):
x = p
return P
P_11 = klasa_p(11)
p_11 = P_11()
assert p_11.x == 11
Przy normalnej definicji klasy, wykonuje się sekwencja poleceń. Na końcu bloku class otrzymujemy
pewien zestaw zmiennych w przestrzeni nazw (namespace), który jest słownikiem. Można też od
razu stworzyć swój namespace, w tym ypadku pewien słownik i za pomocą funkcji type klasę o
odpowieniej nazwie.
B = type('B', (object,), dict(x=11))
assert B().x == 11
Argumentami funkcji type są — nazwa klasy, lista rodziców i treść klasy, czyli słownik. czyli de facto
jej namespace. Wiedząc, że w pythonie można dynamicznie tworzyć klasy tak jak w poniższym
przykładzie:
I kolejne przykłady:
class C(object):
def f(self, x): return x
f = lambda x: None
assert C().f(33) == None
par = "self.y * self.y"
x = "y"
d={}
d[x] = 10
d["action"] = lambda self: eval(par)
Bar = type('Bar',(object,),d)
b = Bar()
print b.y
print b.action()
Treść projektu
Napisz prosty program, który z zadanego opisu XML tworzy klasę. Opis XML może wyglądać np. tak:
<class nazwa="Test">
<attribute nazwa="y" wartosc="10">
</attribute>
<attribute nazwa="x" wartosc="5">
</attribute>
<method nazwa="action" tresc="self.y * self.y">
</method>
<method nazwa="metoda2" tresc="self.x * self.y">
</method>
</class>
W znaczniku class w atrybucie nazwa jest zapisana nazwa klasy. W znacznikach o nazwie "attribute"
w atrybucie "nazwa" jest zapisana nazwa atrybutu klasy, w atrybucie "wartosc" -- jego wartosc. W
znacznikach "method" w atrybucie "nazwa" jest nazwa metody, a atrybucie "tresc" -- jej tresc.
Dopuszczamy wyłącznie bardzo proste metody, bezparametrowe, operujące na atrybutach obiektu i
wyłącznie zwracające wynik tych operacji. Nieprecyzyjności w sformułowaniu należy zinterpretować
na własną korzyść, tzn tak, jak nam ławiej zaimplementować rozwiązanie.
Projekt zaliczeniowy: Temat 1 -- tworzenie klas na podstawie opisu w XML
Rozwiń powyższe zadanie.
Zaprojektuj sposób opisu klasy, uwzględniający możliwie dużo aspektów nie objętych obecną
wersją, np.:
metody z argumentami
zmianę metody init
metody, które coś wypisują
oddzielnie metody klasowe od metod obiektu
oddzielnie atrybuty klasowe od atrybutów obiektu
dziedziczenie
...
Sposób opisu zaprojektowany przez Ciebie może być zupełnie inny niż zaproponowany we wstępie do
zadania. Napisz parser, który na podstawie opisu XML zgodnego z Twoimi "zasadami"
zaimplementuje opisane klasy.
FAQ do Tematu 1
Wstęp do projektu zaliczeniowego: Temat 2
Projekt zaliczeniowy: Temat 2 -- Parsowanie pliku z metaopisem danych
Rozwiń swoją klasę do cięcia danych z pracowni o automatyczne wczytywanie wszystkich możliwych
parametrów, typu częstość próbkowania, nazwy/ilość elektrod z zadanego pliku .xml Format pliku
.xml -- tak jak pliki .info z pracowni. Przy ocenie zadania będzie brana pod uwagę dokładność
implementacji: odporność na błędy, łapanie wyjątków, jakość dokumentacji, itd
FAQ do Tematu 2
Literatura
Korzystałyśmy z:
1. XML in 10 points
2. samouczek do lxml.etree

Podobne dokumenty