Seite 2 von 2
Verfasst: Freitag 22. Mai 2009, 12:03
von BlackJack
Der Sinn ist Rückwärtskompatibilität. Das ``as`` an der Stelle ist relativ neu.
Eine `expression` kann auch ein Tupel ergeben. Die Forderung nach den Klammern kommen, zumindest informell, daher, dass man bei einem Tupel die Klammern braucht, falls es sonst zu Mehrdeutigkeiten kommen kann. Das ist ja hier der Fall wenn man ein ``,`` statt dem ``as`` verwendet.
Verfasst: Freitag 22. Mai 2009, 12:11
von Dill
ok, klar. in python 3 ist es ja auch eindeutig ("," statt "as" ist nichtmehr erlaubt)
Verfasst: Freitag 22. Mai 2009, 15:29
von Leonidas
Dill hat geschrieben:ok, klar. in python 3 ist es ja auch eindeutig ("," statt "as" ist nichtmehr erlaubt)
Und ``as`` ist sowieso erst ab Python 2.6 möglich.
Verfasst: Freitag 22. Mai 2009, 19:18
von problembär
Hallo,
hier mein Vorschlag:
Code: Alles auswählen
#!/usr/bin/env python
#-*- coding: iso-8859-1 -*-
import xml.dom.minidom
def getUglyString(fname):
fh = file(fname, "r")
a = fh.readlines()
fh.close()
b = ""
for i in a:
i = i.strip()
b += i
return b
dom = xml.dom.minidom.parseString(getUglyString("file.xml"))
lines = []
def getLine(merkmalnode):
line = merkmalnode.attributes["ID"].nodeValue
for node in merkmalnode.childNodes:
if node.nodeType == node.ELEMENT_NODE and node.nodeName == "Name":
for u in node.childNodes:
if u.nodeType == u.TEXT_NODE:
line += ";" + u.nodeValue.strip()
if node.nodeType == node.ELEMENT_NODE and node.nodeName == "AddData":
for u in node.childNodes:
if u.nodeType == node.ELEMENT_NODE and u.nodeName == "Wert" and u.attributes["MerkmalID"].nodeValue == "Kurzbezeichnung":
for u2 in u.childNodes:
if u2.nodeType == u.TEXT_NODE:
line += ";" + u2.nodeValue.strip()
if u.nodeType == node.ELEMENT_NODE and u.nodeName == "Wert" and u.attributes["MerkmalID"].nodeValue == "Beschreibung":
for u2 in u.childNodes:
if u2.nodeType == u.TEXT_NODE:
line += ";" + u2.nodeValue.strip()
return line
def walkOver(domina):
for node in domina.childNodes:
if node.nodeType == node.ELEMENT_NODE and node.nodeName == "Merkmal" and node.attributes.has_key("ID"):
lines.append(getLine(node))
walkOver(node)
walkOver(dom)
for i in lines:
print i
Geht das?
Gruß
Verfasst: Freitag 22. Mai 2009, 19:49
von BlackJack
Also rein aus ästhetischen Gründen und aus "pythonischer" Sicht geht das ja mal gar nicht. Pfui, ist das *hässlich*!
Edit: So sieht das IMHO etwas hübscher aus:
Code: Alles auswählen
import csv
import sys
from lxml import etree
def parse(filename):
root = etree.parse(filename)
xpath_template = 'AddData/Wert[@MerkmalID="%s"]/text()'
short_description = xpath_template % 'Kurzbezeichnung'
description = xpath_template % 'Beschreibung'
for merkmal in root.xpath('//Merkmal'):
yield [merkmal.attrib['ID'],
merkmal.find('Name').text,
merkmal.xpath(short_description)[0],
merkmal.xpath(description)[0]]
def main():
writer = csv.writer(sys.stdout, delimiter=';')
writer.writerows(parse('test.xml'))
Verfasst: Freitag 22. Mai 2009, 20:29
von cofi
Na wobei `walkOver(domina)` zumindest Humor zeigt

`minidom` statt `ElementTree` oder `lxml` zu benutzen zeigt auch von masochistischen Ausprägungen

Verfasst: Freitag 22. Mai 2009, 21:05
von Dill
blackjack, hast du eigentlich einen generator fetisch?
hier eine version mit xpath, lxml.objectify und csv.DictWriter
Code: Alles auswählen
import csv
from lxml import objectify
def main(xml_file, csv_file):
tree = objectify.parse(xml_file)
writer = csv.DictWriter(csv_file, ("ID", "Name", "Kurzbezeichnung", "Beschreibung", "Noch_ein_Wert"))
for m in tree.xpath("//Merkmal"):
row = dict()
row.update({"ID": m.get("ID")})
row.update({"Name": m.Name})
for wert in m.AddData.xpath("Wert"):
row.update({wert.get("MerkmalID") : wert})
writer.writerow(row)
if __name__ == "__main__":
import StringIO
xml_file = StringIO.StringIO("""
<root>
<Merkmal ID="34747">
<Name>Volumen</Name>
<AddData>
<Wert MerkmalID="Kurzbezeichnung">Volumen</Wert>
<Wert MerkmalID="Beschreibung">= Inhalt einer Verpackung in Liter</Wert>
</AddData>
</Merkmal>
<Merkmal ID="4711">
<Name>Gewicht</Name>
<AddData>
<Wert MerkmalID="Kurzbezeichnung">Gewicht</Wert>
<Wert MerkmalID="Beschreibung">= Inhalt einer Verpackung in kg</Wert>
<Wert MerkmalID="Noch_ein_Wert">Hallo, Welt!</Wert>
</AddData>
</Merkmal>
</root>""")
csv_file = StringIO.StringIO()
main(xml_file, csv_file)
print csv_file.getvalue()
Code: Alles auswählen
34747,Volumen,Volumen,= Inhalt einer Verpackung in Liter,
4711,Gewicht,Gewicht,= Inhalt einer Verpackung in kg,"Hallo, Welt!"
Verfasst: Freitag 22. Mai 2009, 21:33
von BlackJack
@Dill: Ich denke halt gerne in Datenströmen und Funktionen die welche erzeugen, verändern, oder akkumulieren. Da bieten sich Generatorfunktionen und die `itertools` einfach an, um daraus flexibel Lösungen zusammen zu stecken.
Bei mir ist zum Beispiel die Datenquelle sauber von der Senke getrennt. Die Daten könnten genauso gut aus einer Funktion kommen die eine Datenbank befragt, also auch in eine Funktion gesteckt werden, die die Daten in eine Datenbank schreibt. Oder als JSON schreibt, oder…
Und man könnte auch noch Filterfunktionen dazwischen schalten wenn man möchte.
Verfasst: Samstag 23. Mai 2009, 00:19
von problembär
Der Name "walkOver()" war zwar meine Idee (in Anlehnung an die "
TreeWalker"-Klasse).
Das mit (der) "domina" aber nicht; das kommt von hier:
http://de.wikibooks.org/wiki/Python_unter_Linux:_XML
unter "XML lesen".
Gruß
Verfasst: Samstag 23. Mai 2009, 10:46
von Dill
@blackjack: dein code ist wirklich sehr sexy. ich werde mal drauf achten in zukunft wo sich generatoren anbieten. trotzdem finde ich meinen code hier einfach lesbarer. (was hautpsächlich an DictWriter liegt, dieser verhindert aber auch eine sinnvolle die trennenung von writer und parser und macht daher ein yield(row) weniger sinnig)
wie wärs damit: (das geht sicher auch klarer??)
Code: Alles auswählen
l = [list(chain(*[
(m.get("ID"), m.Name), [wert for wert in m.AddData.xpath("Wert")]
])) for m in tree.xpath("//Merkmal")]
Verfasst: Montag 25. Mai 2009, 10:41
von PhantomWorks
Hallo!
Vielen Dank für Eure Antworten! Ich hatte vergangenes Wochenende ZEit und bin kurz vor der Lösung.
Es gibt nur noch ein einziges Problem:
Wenn ich die Elemente der CSV-Datei die ich in Variablen gespeichert habe mit dem CSV-Writer über writerow schreiben will passiert folgendes: Es steht in jedem Feld nur ein einziger Buchstabe, d.h. ein Wort mit 10 bestehend aus 10 Buchstaben erstreckt sich momentan über 10 Spalten.
Habe schon hin und her probiert, bin jedoch noch auf keinen grünen Zweig gekommen.
Hat einer Rat an was das liegen kann?
Verfasst: Montag 25. Mai 2009, 10:45
von cofi
Dann solltest du mal den Code zeigen, der die CSV-Datei schreibt.
Verfasst: Montag 25. Mai 2009, 10:47
von EyDu
Ich rate mal: du übergibst die Zeile nicht als Tupel sondern irgendwie als String?
Verfasst: Montag 25. Mai 2009, 11:04
von Dill
strings sind tupel von zeichen, daher das seltsame verhalten...
Code: Alles auswählen
In [23]: w.writerow("hallo")
In [24]: w.writerow( ("hallo",) )
In [25]: print o.getvalue()
h,a,l,l,o
hallo
Verfasst: Montag 25. Mai 2009, 20:13
von derdon
Du meinst, Strings sind Sequenzen aus Strings

Verfasst: Dienstag 26. Mai 2009, 08:31
von Dill
wenn schon, dann (immutable) sequenz von chars und escapeseqs (mit evtl. prefix)
Code: Alles auswählen
stringliteral ::= [stringprefix](shortstring | longstring)
stringprefix ::= "r" | "u" | "ur" | "R" | "U" | "UR" | "Ur" | "uR"
shortstring ::= "'" shortstringitem* "'" | '"' shortstringitem* '"'
longstring ::= "'''" longstringitem* "'''"
| '"""' longstringitem* '"""'
shortstringitem ::= shortstringchar | escapeseq
longstringitem ::= longstringchar | escapeseq
shortstringchar ::= <any source character except "\" or newline or the quote>
longstringchar ::= <any source character except "\">
escapeseq ::= "\" <any ASCII character>
du hast angefangen

Verfasst: Dienstag 26. Mai 2009, 08:37
von BlackJack
@Dill: Nein, denn es gibt in Python keine `char`\s [1]_. Die Beschreibung von Zeichenkettenliteralen im Quelltext und die Eigenschaften von `str`-Objekten sind zwei verschiedene paar Schuhe.
Zeichenketten sind eine ("immutable") Sequenz von Zeichenketten der Länge 1, ist denke ich eine treffende Beschreibung, die das Phänomen erklärt.
.. [1] Jaja, ich weiss, es gibt `ctypes.c_char`.
Verfasst: Dienstag 26. Mai 2009, 16:18
von problembär
PhantomWorks hat geschrieben:Vielen Dank für Eure Antworten! Ich hatte vergangenes Wochenende ZEit und bin kurz vor der Lösung.
Schön. Und wie jetzt, mit minidom oder lxml/etree?
(Bei letzterem bin ich raus.)
Gruß
Verfasst: Freitag 29. Mai 2009, 09:59
von PhantomWorks
@problembär: Ich verwende jetzt lxml/etree. Ich habe zwar auch einen Versuch mit minidom gestartet, diesen jedoch bald wieder verworfen.
@all:
Ich habe mir nun die csv.DictWriter Funktion angesehen
csv.DictWriter(csvfile, fieldnames[, restval=''[, extrasaction='raise'[, dialect='excel'[, *args, **kwds]]]])
als auch das was dahinter steckt
Code: Alles auswählen
class DictWriter:
def __init__(self, f, fieldnames, restval="", extrasaction="raise",
dialect="excel", *args, **kwds):
self.fieldnames = fieldnames # list of keys for the dict
self.restval = restval # for writing short dicts
if extrasaction.lower() not in ("raise", "ignore"):
raise ValueError, \
("extrasaction (%s) must be 'raise' or 'ignore'" %
extrasaction)
self.extrasaction = extrasaction
self.writer = writer(f, dialect, *args, **kwds)
self.writer.writerow(fieldnames)
def _dict_to_list(self, rowdict):
if self.extrasaction == "raise":
for k in rowdict.keys():
if k not in self.fieldnames:
raise ValueError, "dict contains fields not in fieldnames"
return [rowdict.get(key, self.restval) for key in self.fieldnames]
def writerow(self, rowdict):
return self.writer.writerow(self._dict_to_list(rowdict))
def writerows(self, rowdicts):
rows = []
for rowdict in rowdicts:
rows.append(self._dict_to_list(rowdict))
return self.writer.writerows(rows)
Mein Code:
Code: Alles auswählen
fieldnames=['A1','A2', 'A3', 'A4', 'A5']
csv.DictWriter((csv_file, 'w'), fieldnames, restval="", extrasaction="raise", dialect="excel", *args, **kwds)
Ich bekomme folgenden Error:
File "C:\Python26\lib\csv.py", line 133, in __init__
self.writer = writer(f, dialect, *args, **kwds)
TypeError: argument 1 must have a "write" method
Wozu brauche ich dieses *args, **kwds? ich habe schon hier im Forum danach gesucht und auch etwas gefunden doch meiner Meinung nach ist dieses für meinen Anwendungsfall nicht notwendig.
Ich habe inzwischen eine Liste erzeugt aus allen Merkmalen, wobei jedes Merkmal (eindeutig durch die ID identifiziert) ein Dictionary darstellt. Ziel ist es nun die Dictionaries anhand der Fieldnames die ich mit den Schlüsseln des Dicts gemappt habe (also z.B. entspricht der Schlüssel Merkmal ID --> A1, Name --> A2 usw.), zeilenweise in die CSV zu schreiben, sodass letztendlich jedes einzelne Dict der Liste eine Zeile der CSV darstellt.
Viele Grüße
Verfasst: Freitag 29. Mai 2009, 10:27
von BlackJack
@PhantomWorks: In der Doku zu
csv.DictWriter kannst Du nachlesen, dass als erstes Argument eine Datei ewartet wird. So wie's aussieht übergibst Du aber ein Tupel mit, keine Ahnung was `csv_file` ist, und einer Zeichenkette. Und so ein Tupel hat im Gegensatz zu einer Datei keine `write()`-Methode.
Wieso übergibst Du die ganzen Argumente mit den gleichen Werten wie sie sowieso schon vorbelegt sind? Was ist an ``*args`` und ``**kwds`` gebunden und *warum*?
Vielleicht magst Du mal das Tutorial zum Thema
“Defining functions“ durcharbeiten.