Konfiguration von Objekten innerhalb einer Klasse

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
ippurk
User
Beiträge: 61
Registriert: Mittwoch 8. Juli 2009, 20:40

Hallo zusammen,

ich hab hier mal was produziert:

Code: Alles auswählen

class Home():
    """
    This is a home
    """
    def __init__(self):
        pass

    def configure(self, **args):
        if "street" in args:
            self.street = args["street"]
        else:
            self.street = None
            
        if "nr" in args:
            self.nr = int(args["nr"])
        else:
            self.nr = None

        if "postcode" in args:
            self.postcode = args["postcode"]
        else:
            self.postcode = None

        if "city" in args:
            self.city = args["city"]
        else:
            self.city = None

    def show_address(self):
        print self.street + " " + str(self.nr)
        print str(self.postcode) + " " + self.city

if __name__ == "__main__":
    home1 = Home()
    home1.configure(street="Highway",
                    nr=12,
                    postcode=45321,
                    city="Big City")
    home1.show_address()
Ich weiß, dass ich die einzelnen Werte der Variablen auch ändern könnte, indem ich

Code: Alles auswählen

home1.nr = 12
schreibe, aber so kommt mir das irgendwie praktischer vor. Was meint ihr ?
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Variable Argumente sind eine nette Sache, aber nur selten wirklich benoetigt und verschleiern zudem die Parameter, die die Funktion wirklich entgegen nimmt.

In deinem Fall kannst du auch einfach

Code: Alles auswählen

def __init__(self, street=None, nr=None, postcode=None, city=None):
schreiben, zumal der Schritt ueber ``configure`` IMHO sinnfrei ist. Praktischer ist der Weg ueber die Initialsierung, solltest du aber dennoch Datensaetze/Objekte haben die sich haeufiger und komplett aendern, kannst du sie trotzdem anbieten - ist letzteres nicht erfuellt ist der direkte Zugriff auf die Attribute weit sauberer.

Btw: ``**args`` heisst konventionell ``**kwargs``, ``args`` nimmt man fuer die Postionsargumente. Und von ``object`` erben sollte man in Python2.x auch ;)
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

ippurk, du kannst `__init__` und `configure` kombinieren und das ganze wesentlich einfacher gestalten. Mit `fromkeys` kannst du ein dict mit einer Liste von Schlüsseln initialisieren, sodass deren Werte `None` sind. Wenn du das an `__dict__` zuweist, hast du automatisch alle deine gewünschten Properties angelegt. Mit `update` kannst du ein dict (also z.B. das mit deinen Properties) mit den Schlüsseln und Werten aus dem angegebenen dict aktualisieren.

Code: Alles auswählen

class Home(object):
    def __init__(self, **kw):
        self.__dict__ = dict.fromkeys("street city".split())
        self.__dict__.update(kw)

h = Home(city="Kiel")
print h.street
print h.city
Stefan
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

sma hat geschrieben:Wenn du das an `__dict__` zuweist, hast du automatisch alle deine gewünschten Properties angelegt. Mit `update` kannst du ein dict (also z.B. das mit deinen Properties) mit den Schlüsseln und Werten aus dem angegebenen dict aktualisieren.
Bloß nicht. Wenn schon Magie, dann auf dem „offiziellen“ Weg.

Code: Alles auswählen

class Home(object):
    def __init__(self, **attributes):
        for attr in ("street", "nr", "postcode", "city"):
            setattr(self, attr, attributes.get(attr, None))
Wenn man direkt auf __dict__ zugreift, dann umgeht man damit eventuell existierende Properties etc.
ippurk
User
Beiträge: 61
Registriert: Mittwoch 8. Juli 2009, 20:40

hm ja, verstehe. Kommt mir auch im Nachhinein relativ umständlich vor das ganze. Ich werd wohl cofis ersten Vorschlag nutzen. Die Idee kam wahrscheinlich daher, daß ich in den letzten Wochen recht viel mit Tkinter gemacht habe. Da schreibt man ja auch immer configure.

Auf jeden Fall Danke für die Erläuterungen.
BlackJack

@ippurk: Bei `Tkinter` liegt das `Tk`, das ist halt so entworfen und die Python-Schnittstelle ist nur eine relativ dünne Schicht über diese API.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Darii hat geschrieben:Bloß nicht. Wenn schon Magie, dann auf dem „offiziellen“ Weg.
__dict__ ist Teil der Sprachspezifikation. Ich sehe nicht, was daran magisch sein soll. Properties bei der Initialisierung zu umgehen kann ein Feature sein. Ich würde auch in Java in einem Constructor niemals die Setter-Methoden benutzen.

Stefan
BlackJack

@sma: Die Unterstriche kennzeichnen es als "magisch", und man sollte so etwas nur verwenden, wenn es keine andere Alternative gibt.

Niemals Setter aus dem Konstruktor aufzurufen oder äquivalent dazu in Python den Weg über die externe API eines Objekts zu meiden ist IMHO keine gute Idee. Angenommen ein Setter bzw. bei Python ein Property macht etwas wichtiges mit dem Wert der gesetzt werden soll, dann müsste man den Code dafür duplizieren wenn man im Konstruktor das gleiche ohne den Setter erreichen will.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

sma hat geschrieben:
Darii hat geschrieben:Bloß nicht. Wenn schon Magie, dann auf dem „offiziellen“ Weg.
__dict__ ist Teil der Sprachspezifikation. Ich sehe nicht, was daran magisch sein soll. Properties bei der Initialisierung zu umgehen kann ein Feature sein. Ich würde auch in Java in einem Constructor niemals die Setter-Methoden benutzen.
Wenn du eine Property umgehen *willst*, sprichst auch nichts gegen ``__dict__``, bloß wenn du es nur einfach so benutzt, kann das unerwartete Nebeneffekte haben. Beispielsweise erkennt der sqlalchemy-Mapper Änderungen am Objekt nicht über ``__dict__`` sondern über Properties, wenn man das nicht bedenkt/weiß darf man da u.U. unnötig lange nach Fehlern suchen.

Hat mich jedenfalls mal über eine Stunde gekostet herauszufinden, warum die Relations die ich über ``__dict__`` in einem Kontruktor gesetzt hatte nicht gespeichert wurde, der Rest den ich über ``instance.attribue = foo`` gesetzt hatte, aber schon. Und das nur weil ich dachte, „Kannst ja auch einfach ``self.__dict__.update(kwargs)`` machen“. Meine Lehre daraus: Finger Weg von __*__ es sei denn, es geht nicht anders.
Antworten