ElementTree (XML) speichert alles in eine Zeile?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

@sirius
Zum Dritten mal, was soll denn der Sinn der Funktionen xmlopen, xmladd oder xmlwrite sein?
Ähm, ich habe die Funktionen in ein Modul ausgelagert. Irgendwie muss ich ja vom Hauptprogramm diese aufrufen?
Und da ist mir nichts besseres eingefallen als Funktionen zu schreiben die mir schon vom Namen her sagen was diese machen.

xmlopen -> erstellt ein Objekt von root-Element (Name bei mir: root) und ein tree-Element (Name bei mir: etree)

xmladd -> Fügt ein Node hinzu. Erwartet 2 Argumente. 1. Arg: Name des anzulegeneden Nodes 2.Arg: Inhalt (text) des Nodes

xmlwrite -> Erstellt die XML-Datei und re-parsed das etree zufor mit prettify. Erwartet 1 Argument: Name der XML-Datei welche erstellt wird

Ich verstehe nicht ganz deine Frage. Ist das falsch? Ich sagte bereits am Anfang: geplant war eine Klasse aus alldem zu bilden.

@Hyperion
etree.ElementTree(root).write(...) -> erstellt/schreibt die XML-Datei?
Die Benamsung ist aber sowieso momentan temporär. Ich habe das geschrieben was mir gerade so eingefallen ist. Wenn ich die Funktionen habe die mir ein Element un ein SubElement erstellen, dann wird alles umgestellt. Wie gesagt: geplant war eine Klasse in welcher ich dann im Konstruktor das Element-Obj und das tree-obj erstelle. Dann habe ich auch Klassenweit bekannte Objekte wo das 'public' enfallen wird.
Das hier soll nur dazu dienen die Methoden von etree schnell kennen zu lernen (quasi quick&dirty).

Zu prettify:
kann ich absolut nichts zu sagen! Ich habe es hier vorgeschlagen bekommen und bin schon heilfroh darüber das ich es auf die Reihe bringen konnte das es funktioniert... Danke dafür nochmals an alle!
Sirius3
User
Beiträge: 18264
Registriert: Sonntag 21. Oktober 2012, 17:20

@Hyperion: Dir ist bewußt, dass minidom auch rekursiv arbeitet?

@kaineanung: also nochmal: xmlopen arbeitet mit global, auch wenn es das nicht machen würde, wird tree nicht gebraucht und somit ist xmlopen = Element. xmladd, ist wie schon geschrieben SubElement(root, name).text = wert, hat also auch keinen Mehrwert, außerdem erscheint root aus dem nichts.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Sirius3 hat geschrieben:@Hyperion: Dir ist bewußt, dass minidom auch rekursiv arbeitet?
Nö. Aber... Iiiiihhh bäääähh :twisted:
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

So, um zu beweisen daß ich mit den komischen Funktionen und Objekte, global noch dazu, wirklich nur experimentieren wollte (ist eben echt schnell und einfach es so zu machen) habe ich jetzt daraus eine Klasse erstellt.
Die ist, hoffe ich, zufriedenstellend zumal wenn man bedenkt was für ein Anfänger ich bin :)

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import xml.etree.ElementTree as ET

class MyXML(object):
    
    def __init__(self, rootname):
        self.__root = ET.Element(rootname)
        self.__element = ET.ElementTree(self.__root)
        
    def add_node(self, nodename, value):
        node = ET.Element(nodename)
        self.__root.append(node)
        node.text=value
    
    def add_subnode(self, subnodename, new_nodename, value=""):
        parent_node = self.__root.find(subnodename)
        tmp_node = ET.SubElement(parent_node, new_nodename)
        if value <> '': tmp_node.text = value
        
    def write(self, xmlFileName):
        self.prettify(self.__root)
        ET.ElementTree(self.__root).write(xmlFileName,encoding="utf-8")
        
    def prettify(self, elem, indent="  ", level=0):
        i = "\n" + level*indent
        if len(elem):
            if not elem.text or not elem.text.strip():
                elem.text = i + indent
            for el in elem:
                self.prettify(el, indent, level+1)
            if not el.tail or not el.tail.strip():
                el.tail = i
        if not elem.tail or not elem.tail.strip():
            elem.tail = i
Kein minidom, keine globalen Objekte, keine mehrfach-importierung, etwas sprechendere Funktionsnamen (Methoden) (wenn es auch nur klitzklein besser ist) usw.

Ach ja, warum ich das mache und nicht gleich 'nur' etree also Objekt in meinem 'Haupt'-Code verwende?
Weil ich dadurch Schritt für Schritt lerne und erkenne was für das aktuell gebrauchte Vorhaben wichtig ist aus der Vielfalt an Methoden von etree.
Und weil ich es 'kapseln' kann um meinen 'Hauptcode' übersichtlicher behalten zu können.

Ich hoffe das ist jetzt nichts schlimmes oder der Gleichen?
Sirius3
User
Beiträge: 18264
Registriert: Sonntag 21. Oktober 2012, 17:20

@kaineanung: was soll das My-Präfix? Was sollen die doppelten Unterstriche? self.__element heißt nicht nur falsch, sondern ist auch unnötig. <> ist schon seit Python 2.0 deprecated. add_node benutzt immer noch kein SubElement.
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

@sirius3
was soll das My-Präfix?
Wie soll ich es denn sonst nennen? XML ist es eben nicht weil es nur etwas speziell für mich angepasstes ist. Liegt dann nahe es 'My' zu benennen + das was es beinhaltet (eben doch ein Teilausschnitt von XML). Ausserdem ist die Benennung ja 'sythax-unkritisch' solange es aussagekräftig ist. Und Geschmackssache....
Was sollen die doppelten Unterstriche?
Ich dachte so definiert man private Eigenschaften und Methoden? Unnd ich will das Node-Objekt und das Tree-Bojekt eben nur privat in der Klasse haben. Was ist daran falsch? Was auch immer es ist: sagt es mir damit ich es in Zukunft besser machen kann. Ich hätte nur gedacht das da nichts falsches drann wäre...
self.__element heißt nicht nur falsch, sondern ist auch unnötig
Sehe ich auch gerade. Ich erstelle ein Member-Objekt und benutze es nirgends weil ich die gewünschten Nodes ja nach Anforderung erstelle und 'was auch immer damich mache'.
Ok, wird entfernt.
<> ist schon seit Python 2.0 deprecated
Oh, ok, habe ich nun in != umgewandelt. Bei mir mixen sich gerade mehrere Programmiersprachen...
add_node benutzt immer noch kein SubElement
Ok, jetzt muss ich zugeben: verstehe ich ganz und gar nicht obwohl du mich immer wieder darauf ansprichst! Warum sollte add_Node ein Subelement benutzen?
Ich erstelle ja kein Sub-Node sondern ein Node der obersten Ebene? Oder muss man das gar nicht so unterscheiden? Wenn nicht, dann kann ich das add_node ja ganz weglassen udn immer add_subnode benutzen mit dem Parent-Element 'root'. Wenn ich mir das so recht überlege: warum eigentlich nicht sofern das so geht? Oder meinst du was anderes?
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Namen sind halt eben kein Ausdruck deiner Kreativitaet, sondern sollen etwas aussagen. Das Problem mit "My" ist, dass es nichts zur Aussagekraft beisteuert.

Man kennzeichnet "private" Namen mit einem (1) fuehrenden Unterstrich, zwei (2) dienen zur Vermeidung von Namenskollisionen bei Mehrfachvererbung.

Daneben `prettify` ist semantisch keine Methode, mache daraus wieder eine Funktion.
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

@cofi
Man kennzeichnet "private" Namen mit einem (1) fuehrenden Unterstrich, zwei (2) dienen zur Vermeidung von Namenskollisionen bei Mehrfachvererbung.
Aha, ok. Also nur ein Unterstrich. Wird erledigt.
Man kennzeichnet "private" Namen mit einem (1) fuehrenden Unterstrich, zwei (2) dienen zur Vermeidung von Namenskollisionen bei Mehrfachvererbung.
Ich habe gedacht mit dem einfachen Unterstrich deutet man eine 'Halb-Private' Variable/Methode an. Das man die sozusagen nicht ändern soll aber darf. Aber ok, habei ch auch diesbezüglich was gelernt...

Bei google habe ich gesucht nach 'python class private variables' und folgendes bei 'stackoverflow' gefunden:
Double underscore. That mangles the name. The variable can still be accessed, but it's generally a bad idea to do so.

Use single underscores for semi-private (tells python developers "only change this if you absolutely must") and doubles for fully private.
Daher dachte ich ich mach mal eine ganz private daraus. Aus der Programmierersprache in der ich halbwegs 'fit' bin kenne ich sowas in der Art ja nicht...
Egal, wird jetzt und in Zukunft umgesetzt :)
Daneben `prettify` ist semantisch keine Methode, mache daraus wieder eine Funktion.
Und das mache ich indem ich self nicht mehr übergebe? Die Funktion erwartet aber dennoch dann ein Parameter mehr... wie deklariere ich die Funktion dann als solche damit sie keien Methode wird?
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Wenn dir der Unterschied zwischen Funktion und Methode nicht klar ist, dann arbeite am besten (nochmal) das Python Tutorial durch.
Das spricht uebrigens auch die privaten Namen an:
https://docs.python.org/3/tutorial/classes.html#private-variables hat geschrieben:“Private” instance variables that cannot be accessed except from inside an object don’t exist in Python. However, there is a convention that is followed by most Python code: a name prefixed with an underscore (e.g. _spam) should be treated as a non-public part of the API (whether it is a function, a method or a data member). It should be considered an implementation detail and subject to change without notice.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

kaineanung hat geschrieben:Bei google habe ich gesucht nach 'python class private variables' und folgendes bei 'stackoverflow' gefunden:
Stackoverflow ist so eine Sache. Das Punktsystem und die Popularität führen dazu, dass viele Leute sehr schnell und entsprechend unüberlegt auf möglichst viel Antworten. Die Qualität der Antworten ist dementsprechend im Laufe der Zeit ziemlich gefallen, vorallem bei Detailfragen im Kontext von irgendwelchen populären Themen. Wenn du sowas hast frag in themenspezifischen Communities nach, für Python halt hier oder auch in IRC Channels wie #python und #python.de im freenode Netzwerk.
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

@cofi

Du meinst sicherlich daß die Funktion im gleichen Modul (Datei) aber ausserhalb der Klasse gespeichert werden soll?
Dann hätte ich ja eine Funktion statt einer Methode. Ich weiß lediglich nicht ob die dann in der Klasse 'sichtbar' wäre. Das muss ich eben ausprobieren oder diese dann als global deklarieren falls nicht sichtbar. Dann buht ihr mich aber aus weil global geht gar nicht...

Und laut python-tutorial sind Funktionen in einer Klasse eben Methoden. Und explizit Funktionen in der Klasse geht ja nicht..... oder habe ich was verpasst? Darum war ich stutzig und mir ist der Unterschied einer Funktion zu einer Methode durchaus bewusst.
BlackJack

@kaineanung: Funktionen in einer Klasse geht schon — nennt sich `staticmethod()`.

Wenn Du die Funktion auf Modulebene definierst dann ist die wie jeder Name auf Modulebene innerhalb des Moduls sichtbar und von aussen entweder per Punkoperator über das Modul oder man kann den Namen explizit importieren.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Du hast `global` nicht verstanden. Mit `global` erreicht man, dass der deklarierte Name nicht im lokalen Namensraum definiert wird, sondern eben im globalen.
`global` ist deswegen "boese", weil man keine globalen veraenderlichen _Daten_ haben will, die sollten Funktionen/Methoden eben ueber Parameter betreten und ueber Rueckgabewerte verlassen.

Das macht mit Funktionen aber keinen Sinn, denn entweder man definiert sie schon global, d.h. auf oberster Ebene, oder sie sollten es eben nie sein, z.B. bei `lambda`s.

Generell gilt: Wenn du dir unsicher bist, ob etwas funktioniert (und es nicht nachschlagen willst oder finden kannst), dann teste es doch einfach ;)
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

@BlackJack

Cool, also war meine vermutung richtig... vielleicht wir ja noch was aus mir :wink:

@cofi

Wie ich habe es nicht verstanden mit dem 'globalen deklarieren'?

Ich denke schon:

Variable im deklarierter Namensraum = sichtbar in diesem Namensraum
Variable ausserhalb des Namensraumes deklariert = nicht sichtbar in diesem Namensraum
Beim Letzteren: ausser es ist global deklariert und dann eben über alle Namensräume sichtbar

Also, sichtbar und nicht sichtbar sind meine Worte....
Und diese definitin stimmt doch so oder nicht?
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Nein das stimmt so nicht. Sichtbar ist alles, was in uebergeordneten Namensraeumen definiert ist.

Code: Alles auswählen

def foo(x):
    def bar()
        return x
    return bar
`x` ist nicht im Namensraum von `bar` definiert, ist aber trotzdem zugaenglich.
Der Punkt bei `global` ist, dass es um das _aendern_ geht, statt im aktuellen Namensraum definierst du damit im globalen Namensraum.

Code: Alles auswählen

def foo():
    global x
    x = 42
Ist eben das gleiche wie wenn ich diese Zuweisung direkt toplevel schreibe.
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

@cofi

Ah ok, ich habe dein 'zugänglich' mit meinem 'sichtbar' übersetzt.
Und ich glaube verstanden zu haben.

Ich schau mal wie ich das heute alles hinbüglen kann...
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

So, ich glaube jetzt habe ich es richtig.
Um beim 'umtippen' wurde es mir auch immer klarer was ihr so meint.

Auch wenn ich immer noch nicht ganz verstehe warum ich in der Klasse keine echten 'private'-Funktionen benutzen soll sondern die 'protected'. Das erste wird laut python-tutorial mit doppelten Unterstrichen deklariert und das Zweite, also die protected-Funktionen, nur mit einem Unterstrich.

private ist laut Tutorial definitiv privat und nicht überladbar in vererbten Klassen und protected eben nur 'semi-privat'. Man soll sie nicht ändern, man kann sie ändern in einer vererbten Klasse.

Nun gut, das ist ja wohl die Philosophie von OOP und dem Überladen und sicherlich ist es dem geschuldet daß ihr mir sagt die Funktionen sollen protected sein, richtig?

Nun gut, hier die Klasse:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import xml.etree.ElementTree as ET

def prettify(elem, indent="  ", level=0):
    i = "\n" + level*indent
    if len(elem):
        if not elem.text or not elem.text.strip():
            elem.text = i + indent
        for el in elem:
            prettify(el, indent, level+1)
        if not el.tail or not el.tail.strip():
            el.tail = i
    if not elem.tail or not elem.tail.strip():
        elem.tail = i

class MyXML(object):
    
    def __init__(self, rootname):
        self._root = ET.Element(rootname)
        
    def add_node(self, nodename, value=""):
        tmp_node = ET.Element(nodename)
        self._root.append(tmp_node)
        if value != '': tmp_node.text = value
    
    def add_subnode(self, subnodename, new_nodename, value=""):
        parent_node = self._root.find(subnodename)
        tmp_node = ET.SubElement(parent_node, new_nodename)
        if value != '': tmp_node.text = value
    
    def move_node(self, node_name_to_move, node_name_target):
        tmp_node_name_target = self._root.find(node_name_target)
        tmp_node_to_move = self._root.find(node_name_to_move)
        self._root.remove(tmp_node_to_move)
        tmp_node_name_target.append(tmp_node_to_move)
        
    def write(self, xmlFileName):
        prettify(self._root)
        ET.ElementTree(self._root).write(xmlFileName,encoding="utf-8")
Ach ja, der Name ist MyXML geblieben. Ist ja meine 'Abstraktion' von etree für XML und da ist mir nichts besseres eingefallen.
ein Node zu verschieben ist nun auch enthalten.
Auch habe ich nun verstanden wie das mit dem Tree und dem Node geht. Node hat erstmal nichts mit dem Tree zu tun sondern ist als ein Element, unabhängig von der eigentlichen XML-Struktur zu betrachten. Auch wenn es weitere Unterelemente beinhalten würde.
Wenn man das Node nun soweit manipuliert und modelliert hat, weist man es dem Tree zu und knüpft es sozusagen in den Baum ein. Um ein vorhandenes Node zu bearbeiten kann man es im Baum finden und auf ein (temporäres) Nodeobjekt referenzieren. Dort bearbeiten (z.B. set(Value) und der Gleichen) und es wird, dank der Referenzierung, in der Baumstruktur geändert.

Das Einzige was ich nicht verstanden habe:
wenn ich ein bestehendes Node (im root) einem temporären Node-Objekt zuweise (sagen wir mal tmpNode) und das wird ja als Referenz gemacht weil sonst ein tmpNode.set(value) den Wert im root (von welchem ich es ja referenziert habe) nicht verändern würde, es dann im root aber lösche (root.remove(tmpNode)), wieso bleibt der ganze Node erhalten in der temporären Node-Referenz und kann es dann woanders (sagen wir einem targetNode welches im root enthalten ist) wieder hinzufügen (targetNode.append(tmpNode))?
Will damit sagen: wenn es eine Referenz ist (ein Pointer) auf das Element, ich das Element aber lösche, dann zeigt der Pointer ja auf nichts mehr? Ich füge diesen aber an anderer Stelle ja trotzdem wieder ein?

Aber erstmal: es funktioniert und es hat mich einen guten Schritt weiter nach vorne gebracht!
Danke euch allen für eure Geduld und Hilfe! :D
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

Ach ja, was ich auch noch fragen wollte:

Wenn ich ein Subnode hinzufüge und gleich danach diesem Subnode ein neues Subnode hinzufügen will, findet etree das neue SubNode vielleicht nicht?

Ich mache folgendes:

Code: Alles auswählen

xml.add_subnode("rootnode","subnode1","Wert1")
xml.add_subnode("subnode1","URL","http://irgendwas")
In der zweiten Zeile bekomme ich einen Fehler:
File "/home/user1/work/tmparser/xmlclass.py", line 30, in add_subnode
tmp_node = ET.SubElement(parent_node, new_nodename)
File "/usr/lib/python2.7/xml/etree/ElementTree.py", line 530, in SubElement
element = parent.makeelement(tag, attrib)
AttributeError: 'NoneType' object has no attribute 'makeelement'
Das deutet für mich darauf hin das er das 'Parentelement' nicht findet.
Das habe ich aber zuvor angelegt und gleich im zweiten Schritt will ich ihm ein weiteres SubElement hinzufügen. Da er das nicht findet (ich mache ja im etree ein find('Nodename')) kann es sein das es noch gar nicht im etree-Objekt vorhanden ist?
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Er findet das Element nicht, weil es eben nicht (dort) vorhanden ist:

Code: Alles auswählen

In [24]: tostring(top)
Out[24]: '<top><x><y><z /></y></x></top>'

In [25]: top.find('z')

In [26]: top.find('x').find('y').find('z')
Out[26]: <Element 'z' at 0x7fd2f6babd50>

In [27]: top.findall('z')
Out[27]: []
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

@Cofi

Ich habe mich wohl von BS irreleiten lassen weil dort die 'find'-Methode, unabhängig der Verkettung der Struktur, das Node findet. etree wohl nicht.

Gibt es eine Möglichkeit das SubElement so zu gestalten das ich ihm nicht n-Nodes (also die ganze Verkettung) übergeben muss?

Wäre ja doof das ich meine Methoden so gestalten muss daß das add_SubElement so viele Parameter hat wie es Nodes in der Verkettung geben könnte... (ich hoffe das war verständlich ausgedrückt)
Antworten