Seite 1 von 1

Lassen sich Instanzattribute vor Instanziierung abfragen?

Verfasst: Freitag 10. Mai 2013, 08:51
von mutetella
Guten Morgen!

Kann ich einer Klasse, z. B.

Code: Alles auswählen

class Morning(object):
    def __init__(self, coffee=True, beer=False):
        self.coffee = coffee
        self.beer = beer
die Attributnamen `coffee` und `beer` entlocken?

Momentan würde ich es so

Code: Alles auswählen

class Morning(object):
    NAMES = ('coffee', 'beer')
    ...
machen. Gibt es noch 'ne andere Möglichkeit?

mutetella

Re: Lassen sich Instanzattribute vor Instanziierung abfragen

Verfasst: Freitag 10. Mai 2013, 09:07
von Sirius3
Hallo mutetella,
woher soll denn Python wissen, was in __init__ gemacht wird, bevor es __init__ ausführt?
Um sagen zu können, wie Du das Problem am besten angehst, solltest Du uns noch verraten, was Du mit den Attributen überhaupt anstellen willst.

Re: Lassen sich Instanzattribute vor Instanziierung abfragen

Verfasst: Freitag 10. Mai 2013, 09:37
von xeike
Suchst du so etwas hier:

Code: Alles auswählen

[element for element in dir(breakfast) if element not in dir(Morning)]
Mit set() würde das sicher auch eleganter gehen.

Xe

Re: Lassen sich Instanzattribute vor Instanziierung abfragen

Verfasst: Freitag 10. Mai 2013, 09:50
von mutetella
@Sirius3
Während ich gerade versucht habe, Dir mein Problem zu schildern, kam ich selbst auf die Lösung. Ich war mal wieder nicht in der Lage, aus 2 Richtungen zu denken... :wink:
Ich habe eine `Recurrence` Klasse und leite von dieser `Daily`-, `Weekly`-, und `MonthlyRecurrence` ab. Neben den Basisattributen von `Recurrence` haben die Unterklassen jeweils weitere Attribute. Zum Erstellen einer monatlichen Wiederholung bekomme ich vom Parser z. B.

Code: Alles auswählen

{'recurrence': 'm', 'interval': '2', 'weekday': 'mo', 'weekday_position': '0'}
Um daraus ein Exemplar von `MonthlyRecurrence` zu erstellen wollte ich über dessen Attribute gehen und mit dem dictionary abgleichen. Natürlich kann ich aber auch über das dictionary gehen und nicht vorhandene Attribute in der Klasse handeln... blöd von mir!

@xeike
Nein, das suche ich nicht. Zumal ich ursprünglich versucht hatte, an die Attributnamen eines potentiellen Exemplares `breakfast` zu gelangen. Würde `breakfast` bereits existieren, würde ich zum Abfragen von Attributnamen auch nicht `dir()` sondern `__dict__` verwenden.

mutetella

Re: Lassen sich Instanzattribute vor Instanziierung abfragen

Verfasst: Freitag 10. Mai 2013, 10:03
von Sirius3
@mutetella: so etwas löst man in Python einfach, in dem man versucht ein Objekt mit diesen Keywords zu erstellen:

Code: Alles auswählen

class MonthlyRecurrence(object):
    def __init__(interval, weekday, weekday_position):
        pass

RECURRENCES = {
    'd':DailyRecurrence,
    'w':WeeklyRecurrence,
    'm':MonthlyRecurrence,
}

args = {'recurrence': 'm', 'interval': '2', 'weekday': 'mo', 'weekday_position': '0'}
recurrence = RECURRENCES[args.pop('recurrence')](**args)
Jetzt fehlt nur noch das Exceptionhandling um bei TypeError und KeyError eine sinnvolle Fehlerbehandlung zu machen.

Re: Lassen sich Instanzattribute vor Instanziierung abfragen

Verfasst: Freitag 10. Mai 2013, 11:43
von xeike
mutetella hat geschrieben:Würde `breakfast` bereits existieren, würde ich zum Abfragen von Attributnamen auch nicht `dir()` sondern `__dict__` verwenden.
Ah, danke, wieder was dazu gelernt. :)

Xe

Re: Lassen sich Instanzattribute vor Instanziierung abfragen

Verfasst: Freitag 10. Mai 2013, 12:22
von BlackJack
@xeike: Lern das lieber nicht denn `dir()` ist der offzielle Weg mit dem man an die Attribute kommt. `__dict__` funktioniert nur bei Typen die auch tatsächlich ein solches Attribut besitzen und berücksichtigt auch nicht das Ergebnis einer eventuell vorhandenen `__dir__()`-Methode. Wenn es zu einem direkten Zugriff auf einen „__magic__”-Namen eine Alternative gibt, sollte man in der Regel die Variante mit weniger Magie wählen.

Re: Lassen sich Instanzattribute vor Instanziierung abfragen

Verfasst: Freitag 10. Mai 2013, 13:38
von mutetella
@BlackJack:
Wenn ich die Attributnamen eines Klassenexemplares abfragen möchte, dann bevorzuge ich `__dict__`, da ich dann nicht prüfen muss, ob es sich um ein Klassenattribut oder ein mit dem Exemplar erzeugtes Attribut handelt.
Und solange eine Klasse nicht mit `__slots__` hantiert, besitzt das Exemplar doch auch ein `__dict__`, oder?

Welchen Weg gehst Du, wenn Du Attributnamen eines Exemplares erhalten möchtest?

mutetella

Re: Lassen sich Instanzattribute vor Instanziierung abfragen

Verfasst: Freitag 10. Mai 2013, 13:53
von BlackJack
@mutetella: `__slot__`\s sind eine Möglichkeit, in C implementierte Datentypen, die sich nicht die Mühe machen ein `__dict__` zur Verfügung zu stellen eine andere. Und dann gibt es ja noch `__getattr__()` und `__getattribute__()` die einem entgehen können.

Und das bezieht sich jetzt auf CPython. Soweit ich weiss ist `__dict__` kein garantiertes Attribut, es kann also auch Python-Implementierungen komplett ohne dieses Attribut geben.

Attributnamen: `dir()`.

Re: Lassen sich Instanzattribute vor Instanziierung abfragen

Verfasst: Samstag 11. Mai 2013, 12:40
von mutetella
@BlackJack
Wenn ich Dich richtig verstehe,
  • gibt es keinen allgemein gültigen Weg, um ausschließlich an Attribute eines Klassenexemplares zu gelangen, so wie es, falls vorhanden, via `__dict__` möglich ist.
  • liegt es am Entwickler, über ein Klassenattribut oder die Verwendung von `__slots__` eine Möglichkeit zu schaffen, Attributnamen potentieller Exemplare abrufbar zu machen.
Zum Beispiel:

Code: Alles auswählen

class Miracle(object):
    NAMES = ('foo', 'bar')
    def __init__(self, foo, bar):
        self.foo = foo
        self.bar = bar
oder

Code: Alles auswählen

class Miracle(object):
    __slots__ = ('foo', 'bar')
    def __init__(self, foo, bar):
        self.foo = foo
        self.bar = bar
Inwieweit mir `__getattr__()` / `__getattribute__()` bei der Ermittlung eines Attributnamens weiterhilft, verstehe ich nicht.

mutetella

Re: Lassen sich Instanzattribute vor Instanziierung abfragen

Verfasst: Samstag 11. Mai 2013, 13:21
von Sirius3
@mutetella: es macht keinen Unterschied, ob es sich um ein Klassenattribut oder ein Attribut einer Instanz der Klasse handelt. Solange ich Attribute nur lese, verhalten sie sich absolut gleich, wenn ich ein Attribut schreibe, wird es immer als Attribut der Instanz geschrieben. Mit »__dict__« nimmst Du Dir nur die Freiheit, die Implementierung der Klasse irgendwie zu verändern (und sei es nur aus einem Attribut ein Property zu machen).

Re: Lassen sich Instanzattribute vor Instanziierung abfragen

Verfasst: Samstag 11. Mai 2013, 13:33
von BlackJack
@mutetella: `__getattr__()` und `__getattribute__()` helfen Dir nicht weiter Attributnamen zu ermitteln sondern die können auch Attribute zur Verfügung stellen die man nicht über `__dict__` ermitteln kann. Properties hatten wir auch noch nicht angesprochen. Da es diese vielen verschiedenen Wege gibt Attribute zu setzen, ist IMHO der sauberste, weil zuverlässigste, Weg so etwas wie `Miracle.NAMES`. Explizit vs. Implizit.

Re: Lassen sich Instanzattribute vor Instanziierung abfragen

Verfasst: Sonntag 12. Mai 2013, 21:17
von snafu
Sirius3 hat geschrieben:

Code: Alles auswählen

class MonthlyRecurrence(object):
    def __init__(interval, weekday, weekday_position):
        pass

RECURRENCES = {
    'd':DailyRecurrence,
    'w':WeeklyRecurrence,
    'm':MonthlyRecurrence,
}

args = {'recurrence': 'm', 'interval': '2', 'weekday': 'mo', 'weekday_position': '0'}
recurrence = RECURRENCES[args.pop('recurrence')](**args)
So in der Art hätte ich es auch vorgeschlagen. Das Dispatching hätte ich allerdings ein bißchen anders gemacht:

Code: Alles auswählen

RECURRENCES[identifier](**options)
Das ist IMHO halbwegs sauber und kommt ohne die problematische Introspektion aus.

Re: Lassen sich Instanzattribute vor Instanziierung abfragen

Verfasst: Montag 13. Mai 2013, 06:55
von mutetella
@snafu
Weshalb kommt Dein `identifier` ohne Introspektion aus? Irgendwo muss der Verweis ja erstellt werden? Oder meinst Du "problematisch" im Blick auf eine Fehlerbehandlung?

mutetella

Re: Lassen sich Instanzattribute vor Instanziierung abfragen

Verfasst: Montag 13. Mai 2013, 07:38
von Sirius3
@mutetella: wenn Du Python einfach prüfen läßt, ob alle Optionen von __init__ Deiner Klasse akzeptiert werden mußt Du nicht selbst einen Mechanismus entwickeln, die Attribute der Klasse herauszufinden. Alternativ kannst Du auch eine Klassenmethode »create_from_options« definieren, um das Erzeugen der Objekte mit externen Parametern sauber von einem internen Aufruf zu trennen.

Re: Lassen sich Instanzattribute vor Instanziierung abfragen

Verfasst: Montag 13. Mai 2013, 11:33
von snafu
@mutella: Ja klar wird von Python am Ende auch Introspektion verwendet, um auf die Instanzattribute zuzugreifen. Dies verbirgt sich ja letztlich nur hinter einer höheren Abstraktionsschicht. Und genau diese höhere Schicht sollte man als Programmierer benutzen. Das was du versuchst (quasi "manuelle" Introspektion), ist nur unnnötig kompliziert und fehleranfällig. Ich sehe da keinen Mehrwert gegenüber dem "offiziellen" Weg des gezeigten Dictionary-Unpackings via `**options`. Warum sollte man sich Probleme machen, die nicht vorhanden wären, wenn man sich an gewisse Standards halten würde...?

Ich verstehe auch überhaupt nicht, warum man in diesem Zusammenhang schon vor der Erstellung einer Instanz etwas über die zu erwartenden Attribute wissen muss. Eine Klasse hat normalerweise eine klar definierte Schnittstelle. Durch Typprüfung bzw aufgrund des Klassennamens kann man daher eine recht klare Aussage über die Art und Reihenfolge der Parameter machen, die für eine Initialisierung der Klasse nötig sind (also z.B. was an `__init__` übergeben werden muss). Diese Information sollte ein Anwender deines Programms kennen, meist indem er die Dokumentation liest. Wenn er dann sagt "ich möchte jetzt ein Objekt vom Typ XY erzeugen", dann muss eigentlich klar sein, dass von ihm auch die dementsprechend korrekten Parameter erwartet werden. Der übliche Weg wäre es, diese vom Anwender übergebenen Parameter "auf doof" an die Initialisierungsmethode der passenden Klasse weiterzureichen. Kommt es dann bei der Initialisierung zu irgendwelchen Schwierigkeiten, würde die Klasse eine Exception werfen. Und mit dieser Exception könnte man dann auf geeignete Weise umgehen. Bei einem Kommandozeilen-Tool würde man vielleicht eine angepasste Meldung in STDERR schreiben, oder sowas, damit der Benutzer nicht direkt mit den "nackten" Python-Tracebacks in Berührung kommt. Diese Meldung sollte sich aber aus dem Umstand ergeben, dass man dem Inhalt der Exception vertraut und *nicht*, dass man vorher die Parameter auf Korrektheit geprüft hat. Wenn man dies nämlich vorher tut, dann hat man an 2 verschiedenen Stellen im Programm eine solche Prüfung: Einmal innerhalb der Initalisierungsmethode der Klasse, wo Python einen gewissen Teil selbst übernimmt und man vielleicht zusätzlich noch ein paar Typ- und Wertprüfungen dazuprogrammiert hat. Und dann noch *zusätzlich* eine Vorab-Prüfung, weil man schlau sein wollte und aufgrund von Inspizierung der Instanzattribute irgendwelche allgemeinen Fehlermeldungen a la "Parameter `abc` ist nicht vorhanden" für den Anwender ausgibt. Ich nehme doch mal an, dies ist der Hintergrund deines Vorhabens, oder bin ich auf dem falschen Dampfer?