Instanz soll sich mit del() selbst löschen

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.
Antworten
Tomekk81
User
Beiträge: 2
Registriert: Dienstag 15. Juli 2008, 16:00

Hallo alle zusammen,

ich habe in kleines Problem. Ich habe eine Klasse geschrieben, die eine IP-Adresse abbilden soll. Sie überprüft einen String beim Übergeben auf "IP-Adressen-Tauglichkeit" und speichert diesen nur, wenn die IP-Adresse korrekt ist.

Wenn ich nun beim Erzeugen einer neuen Instanz einen falschen String übergebe, z.B "ip = IpAddress('a.b.c.d')", wird zwar die Exception des falschen Strings in der Methode __init__ abgefangen, die Instanz mit del(self) jedoch nicht gelöscht. Wenn ich im Anschluss die Methode __str__ aufrufe, erhalte ich eine Fehlermeldung, da ja keine Variable __ipaddr existiert:

Code: Alles auswählen

Python 2.5.1 (r251:54863, Jun 15 2008, 18:24:51)
[GCC 4.3.0 20080428 (Red Hat 4.3.0-8)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import dhcp
>>> ip = dhcp.IpAddress('a.b.c.d')
>>> ip
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "dhcp.py", line 16, in __str__
    return self.__ipaddr
AttributeError: 'IpAddress' object has no attribute '_IpAddress__ipaddr'
>>>
Da auf die Instanz nur eine Referenz zeigt, sollte Sie doch nach Aufruf von del(self) vom Garbage Collector gelöscht werden, oder?!

Hat einer eine Idee?

Code: Alles auswählen

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

from re import match

class IpAddress(object):
    ipregex = r"^([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])$"

    def __init__(self,ipaddr='0.0.0.0'):
        try:
            self.setIp(ipaddr)
        except ValueError:
            del(self)

    def __str__(self):
        return self.__ipaddr

    def setIp(self,ipaddr):
        try:
            assert match(self.__class__.ipregex,ipaddr)
            self.__ipaddr=ipaddr
        except AssertionError,args:
            raise ValueError, "String is not a IP-Address!"

    getIp = __repr__ = __str__
Nocta
User
Beiträge: 290
Registriert: Freitag 22. Juni 2007, 14:13

Wie man eine Instanz sich selbst löschen lässt, weiß ich grad auch nicht.
Aber imo ist das eher die Aufgabe des Programmes und nicht der Klasse.
Du kannst ja einen Error raisen und diesen dann abfangen.
Wenn du den ValueError nicht abfängst, sondern das erst im Programm machst, könnte das schon reichen ;)
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Das sieht irgendwie nach dem Buch aus dem Galileo-Verlag aus Oo
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Das ist doch quatsch. Wenn in __init__ eine Exception geworfen wird, dann kann man keine Referenz zu der Instanz erzeugen und das heißt dass die Instanz früher oder später sowieso vom Garbage Collector aufgesammelt wird. Man muss in Python unbenötigte Elemente nicht von Hand löschen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

@Tomekk81: Wenn Du nur überprüfen willst, ob eine Zeichenkette eine gültige IP-Adresse sein kann, dann solltest Du eine Funktion schreiben. Diese Klasse ist alleine schon Overkill, und so kompliziert wie Du das gemacht hast nochmal doppelt so schlimm.

Nur mal angenommen man könnte ein Exemplar sich selber löschen lassen, was sollte denn dann Zeile 7 in der interaktiven Sitzung als Ergebnis haben? In Zeile 6 wird ja der Name `ip` and ein Exemplar vom Typ `IpAddress` gebunden. Damit gibt's auch eine Referenz auf das Objekt.

Die Frage ist, warum die Ausnahme in der `__init__()` überhaupt behandelst. Wie soll der Code der das aufruft denn mitbekommen, dass die Zeichenkette falsch war?

Weitere Anmerkungen zum Quelltext: ``del`` ist keine Funktion, sondern eine Anweisung. Und damit löscht man keine Objekte sondern nur Namen und Referenzen auf Objekte. Die Objekte verschwinden automatisch, wenn sie nicht mehr durch Namen oder Referenzen erreichbar sind.

Getter und Setter sind "unpythonisch", in der Regel benutzt man für "berechnete Attribute" Properties, siehe die `property()`-Funktion.

Attribute mit führenden doppelten Unterstrichen werden verwendet um Namenskollisionen bei Unterklassen oder Mehrfachvererbung zu vermeiden, *nicht* als "private"-Ersatz. Als "private", also Implementierungsdetail, kennzeichnet man Attribute durch *einen* führenden Unterstrich.

Der Quelltext von `setIp()` ist etwas umständlich durch die Indirektion mit dem `AssertionError`. Und man sollte sich auch im klaren darüber sein, dass ``assert`` nicht zwangsläufig ausgeführt werden muss. Das ist ein Mechanismus zur Fehlersuche und kann abgeschaltet werden. Bei ``assert``-Anweisungen sollte man also nie etwas schreiben, was wichtig für ein korrektes Funktionieren des Programms ist.

`self.__class__.SPAM` hat den selben Effekt wie das kürzere `self.SPAM`, wenn es kein Attribut mit dem Namen auf dem Exemplar gibt. Dann wird als nächstes nämlich automatisch bei der Klasse nachgeschaut.

Die Form wie Du Ausnahmen auslöst, sollte nicht mehr verwendet werden. Ausnahmen können die Nachricht auch als Argument entgegennehmen.

Wenn man denn so einen unpythonischen Setter schreibt, könnte der so aussehen:

Code: Alles auswählen

    def set_ip(self, ip_address):
        if not re.match(self.IP_RE, ip_address):
            raise ValueError('string is not an IP address')
        self._ip_address = ip_address
Allerdings sollte man entweder den regulären Ausdruck kommentieren, oder vielleicht das ganze als verständlicheren Code schreiben.

Anstelle der Klasse könnte man zum Beispiel diese Funktion schreiben (ungetestet):

Code: Alles auswählen

def is_ip_address(ip_address):
    parts = ip_address.split('.')
    try:
        return len(parts) == 4 and all(0 <= int(p) < 256 for p in parts)
    except ValueError:
        return False
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

Oder als schicker Einzeiler:

Code: Alles auswählen

def is_ipv4(ip):
    return sum(0 <= int(x) <= 255 for x in filter(str.isdigit, str(ip).split("."))) == 4
TUFKAB – the user formerly known as blackbird
Tomekk81
User
Beiträge: 2
Registriert: Dienstag 15. Juli 2008, 16:00

Erst einmal vielen Dank für die vielen Antworten.

Es ist mir klar, dass ich eine Funktion erstellen kann, die überprüft, ob ein String eine IP-Adresse ist. Ich brauche aber einen Typ “IpAdresse“, zu dessen Klasse ich noch weitere Methoden hinzufügen kann…

Ich habe die Klasse jetzt folgendermaßen abgeändert:

Code: Alles auswählen

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

from re import match

class IpAddress(object):
    ipregex = r"^([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])$"

    def __init__(self,ipaddr='0.0.0.0'):
            self.set_ip(ipaddr)

    def __str__(self):
        return self._ipaddr

    def set_ip(self,ipaddr):
        if not match(self.ipregex,ipaddr):
            raise ValueError('String is not a IP-Address!')
        self._ipaddr=ipaddr

    get_ip = __repr__ = __str__
Da ich die Exception nun in __init__ nicht mehr behandle, wird sie bei einem Fehler nach oben weitergereicht und die Instanz nicht erzeugt...Problem gelöst!

Vielen Dank an Alle!

@audax: Nö, ich nutze das Buch „Core Python Programming“ von Wesley J. Chun, sehr zu empfehlen…

@BlackJack: Vielen Dank für die Verbesserungen, jede Sprache hat halt so ihre Eigenheiten ;)

Grüße
Tomekk81
Benutzeravatar
BlackVivi
User
Beiträge: 762
Registriert: Samstag 9. Dezember 2006, 14:29
Kontaktdaten:

Wenn du schon getter und setter verwenden willst, und damit auch gegen die Sprache arbeiten willst, solltest du zumindest die nehmen, die Python einem vorsieht.

Lies mal hier: http://www.python.org/download/releases/2.2/descrintro/

Bei Properties gibt es ein Beispiel für einen pythonischen getter und setter.
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Tomekk81 hat geschrieben: @audax: Nö, ich nutze das Buch „Core Python Programming“ von Wesley J. Chun, sehr zu empfehlen…
Uhm....anscheinend nicht. :D

€dit:
Vergiss erstmal, dass es __foo gibt und nutze für Implementationsdetails _foo, außerdem scheint das Buch doof zu sein ;)
Zap
User
Beiträge: 533
Registriert: Freitag 13. Oktober 2006, 10:56

audax hat geschrieben:außerdem scheint das Buch doof zu sein ;)
@audax: Urteilst du nur oder kennst du das Buch auch!? ;)
Also ich hab das Buch gelesen und bin schon davon überzeugt das es eine sehr gute Hilfe ist um die fundamentalen Konzepte von Python zu verstehen.
Es ist nicht umbedingt ein Buch in dem man unmengen von "echten" Beispielen findet aber es wird sehr detailiert beschrieben wie die Grundkonzepte funktionieren. (Siehe auch http://www.amazon.com/Core-Python-Progr ... 0132269937 )

Die pauschale Aussage: "Vergiss erstmal, dass es __xxx gibt" halte ich für falsch.
IMHO hat es sich erst im laufe der Zeit in die Richtung entwickelt das man __xxx nur in Ausnahmen verwenden sollte. Selbst in Büchern wie DiveIntoPython findet man unter der Überschrift "Private Functions" diese Syntax und keinen Hinweis darauf diese nicht zu verwenden.

Wenn man aus einer anderen Sprache kommt sucht man halt nach so Begriffen wie "Private Member". Ich finde da darf man allen Umsteigern keinen Vorwurf machen.
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

Zap hat geschrieben:Die pauschale Aussage: "Vergiss erstmal, dass es __xxx gibt" halte ich für falsch.
IMHO hat es sich erst im laufe der Zeit in die Richtung entwickelt das man __xxx nur in Ausnahmen verwenden sollte. Selbst in Büchern wie DiveIntoPython findet man unter der Überschrift "Private Functions" diese Syntax und keinen Hinweis darauf diese nicht zu verwenden.

Wenn man aus einer anderen Sprache kommt sucht man halt nach so Begriffen wie "Private Member". Ich finde da darf man allen Umsteigern keinen Vorwurf machen.
Es geht ja nicht um Vorwürfe, sondern darum, sich nichts anzugewöhnen, was in Python keinen wirklichen Sinn macht.
Es gibt eben keine echten "private members" (auch die __foos kann man über kleine Umwege aufrufen).
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Einen Vorwurf nicht, aber ein Buch sollte schon vermitteln, wie man Probleme in der Sprache angeht und nicht nur, wie die Syntax für die Sprache lautet ;)

Zu speziel dem Problem verweist Dive into Python übrigens auf die offizielle Doku, in der es lautet:
Name mangling is intended to give classes an easy way to define ``private'' instance variables and methods, without having to worry about instance variables defined by derived classes, or mucking with instance variables by code outside the class. Note that the mangling rules are designed mostly to avoid accidents
Und dort steht dann schon ganz gut, wofür private Members gut sind...
Und man brauch sie quasi nie. Mir ist davon jedenfalls noch kein Fall untergekommen.

Versteh mich nicht falsch, das ist alles nicht böse gemeint: Ich habs doch am Anfang genauso gemacht und wurde drauf hingewiesen. Dafür postet man doch auch Code ;)

Was ich übrigens Empfehlen kann ist das Python-Büchlein vom Regionalen Rechenzentrum Niedersachsen :D
Abgesehen davon, dass sie immer ``print("foo")`` schreiben ist das höchst gut das Buch.
http://www.rrzn.uni-hannover.de/buch.ht ... tel=python
Keine Ahnung, wie du da rankommen sollst :D
Antworten