html parser
Der Traceback sieht so aus, als ob du lxml für Python > 2.5 installiert hast, und Python 2.5 verwendest, b"" funktioniert erst ab Python 2.6, darunter erzeugt es einen SyntaxError.
- birkenfeld
- Python-Forum Veteran
- Beiträge: 1603
- Registriert: Montag 20. März 2006, 15:29
- Wohnort: Die aufstrebende Universitätsstadt bei München
Die Frage ist, warum überhaupt io.py mitspielt. Das ist in 2.6 zwar vorhanden, aber wird nicht für normales File-I/O verwendet.
@OP: Hast du vielleicht Python 2 und 3 vermischt?
@OP: Hast du vielleicht Python 2 und 3 vermischt?
Wenn man will, kann man natürlich auch XHTML mit regulären Ausdrücken parsen, denn so schwer ist das nicht. Hier habe ich einen Parser für ein Subset von XHTML in ~25 Zeilen Python (Funktion `Html`). Was noch fehlt, ist das Auswerten von Entities. Das war mir zu mühsam. Hier ist ein Vorschlag:
Stefan
Code: Alles auswählen
ENT = re.compile(r'&(?:(\w+)|#([0-9]+)|#x([a-fA-F0-9]+));')
PREDEF = {'quot': '"', 'amp': '&', 'apos': "'", 'lt': '<', 'gt': '>'}
def parse_entities(text):
def parse(m):
if m.group(1): return PREDEF[m.group(1)]
if m.group(2): return unichr(int(m.group(2), 10))
if m.group(3): return unichr(int(m.group(3), 16))
return ENT.sub(parse, text)
@Stefan: Wenn du eine Ausnahme auslöst ohne ihr Argumente zu übergeben, solltest du trotzdem erst ein Exemplar erstellen. In deinem Beispiel wird die Klasse "ausgelöst". Das funktioniert zwar trotzdem (es wird automatisch ein Exemplar erstellt), aber das ist dann doch magisches Verhalten. Problematisch wird das spätestens hier:
Ohne Einblick in den genauen Quelltext weiß man zb nicht, ob der Fehler direkt von MyError ausgeht oder ob MyError ein Name für ein anderes Exemplar einer Exception ist. Zum Beispiel TypeError:
Ich würde mich nicht darauf verlassen, das dieses Verhalten in Python 3 (das ist Python 2.5.4) oder späteren Python Versionen so bleibt.
Code: Alles auswählen
>>> class MyError(Exception):
... def __init__(self, message):
... self.message = message
... def __str__(self):
... return self.message
...
>>> raise MyError
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __init__() takes exactly 2 arguments (1 given)
Code: Alles auswählen
>>> try:
... raise MyError
... except TypeError, MyError:
... raise MyError
...
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
TypeError: __init__() takes exactly 2 arguments (1 given)
>>> MyError.__class__
<type 'exceptions.TypeError'>
- birkenfeld
- Python-Forum Veteran
- Beiträge: 1603
- Registriert: Montag 20. März 2006, 15:29
- Wohnort: Die aufstrebende Universitätsstadt bei München
Das ist weder magisch, noch schlechter Stil.str1442 hat geschrieben:@Stefan: Wenn du eine Ausnahme auslöst ohne ihr Argumente zu übergeben, solltest du trotzdem erst ein Exemplar erstellen. In deinem Beispiel wird die Klasse "ausgelöst". Das funktioniert zwar trotzdem (es wird automatisch ein Exemplar erstellt), aber das ist dann doch magisches Verhalten.
Klar, mit solchen Exceptions macht man das nicht. Aber da geht auch ``raise MyError()`` nicht, so what?Problematisch wird das spätestens hier:
Code: Alles auswählen
>>> class MyError(Exception): ... def __init__(self, message): ... self.message = message ... def __str__(self): ... return self.message ... >>> raise MyError Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: __init__() takes exactly 2 arguments (1 given)
Sowas ist ja auch ähnlich einzuordnen, wie wenn du deine Liste "list" nennst.Ohne Einblick in den genauen Quelltext weiß man zb nicht, ob der Fehler direkt von MyError ausgeht oder ob MyError ein Name für ein anderes Exemplar einer Exception ist. Zum Beispiel TypeError:
Code: Alles auswählen
>>> try: ... raise MyError ... except TypeError, MyError: ... raise MyError ... Traceback (most recent call last): File "<stdin>", line 4, in <module> TypeError: __init__() takes exactly 2 arguments (1 given) >>> MyError.__class__ <type 'exceptions.TypeError'>
Ich würde es (im Falle Python 3) einfach ausprobieren...Ich würde mich nicht darauf verlassen, das dieses Verhalten in Python 3 (das ist Python 2.5.4) oder späteren Python Versionen so bleibt.
Ich bin etwas spät:
Würde man auf solche Spezialfälle in normalen Python Funktionen durchgängig implementieren, wäre das schlechter Stil. Diese Funktion täte etwas, was von ihrer Aufgabe her nicht direkt ersichtlich ist, und tut es nur wenn man einen ganz bestimmten Typ als Argument übergäbe. Und zugleich hat man eine gewisse unnötige Inkonsequenz, denn ansonsten instanziiert man die Klasse ja sowieso. Warum also bei diesem speziellem Fall nicht auch? Warum kracht es nicht einfach, warum versucht raise auf diese Art und Weise das "falsche" Argument zurechtzubiegen?
Das alles ist natürlich nicht so tragisch, wenn man einmal das Verhalten von raise kennt. Dennoch finde ich es allein schon wegem dem sonst üblichem Ducktyping besser, genau das zu übergeben, was auch erwartet wird.
raise verhält sich aktuell wie eine Funktion, die explizit auf einen Typ prüft und dann anders handelt als sonst, also Ducktyping ignoriert.Das ist weder magisch, noch schlechter Stil.
Code: Alles auswählen
def f(thing):
if isinstance(thing, type) and issubclass(thing, Exception):
thing = thing()
# ...
Klar, mit solchen Exceptions macht man das nicht. Aber da geht auch ``raise MyError()`` nicht, so what?
Ich erwarte (bei einer Funktion) normalerweise, das diese Funktion genau beschreibt, wie es mit seinen Argumenten umgeht und welche Verhaltensweisen diese unterstützen müssen. raise als Statement erwartet ein Exemplar einer Subklasse von Exception oder von Exception selbst. Übergebe ich einfach nur einen Namen, erwarte ich (bei Betrachten des Tracebacks zb), das dieser Name diese Verhaltensweisen unterstützt. Insofern wäre der einzige Hinweis darauf, das ich hier tatsächlich eine Klasse übergebe, die übliche Python Namenskonvention. Ohne diese (und das wissen, was raise genau tut) hätte ich nicht den geringsten Anhaltspunkt, was an der Stelle schief gelaufen wäre, weil eben durch einen Typcheck und einer aus der Reihe fallenden Behandlung versucht wurde, den "Fehler" bei Übergabe des Arguments zu korrigieren.Sowas ist ja auch ähnlich einzuordnen, wie wenn du deine Liste "list" nennst.
Ich sagte ja nur, das ich mich selbst nicht darauf verlasse, das es so bleibt bzw es einfach klarer finde, auch hinter ein einzelnes "raise ValueError" noch ein Klammernpaar hinterzusetzen .Ich würde es (im Falle Python 3) einfach ausprobieren...
Das alles ist natürlich nicht so tragisch, wenn man einmal das Verhalten von raise kennt. Dennoch finde ich es allein schon wegem dem sonst üblichem Ducktyping besser, genau das zu übergeben, was auch erwartet wird.