Conditional expression um 3. Abfrage erweitern...?

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
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Code: Alles auswählen

s = 'a'
if s == 'a':
    print s
else:
    print 'b'
lässt sich ja seit Python 2.5 auch so notieren:

Code: Alles auswählen

print 'a' if s == 'a' else 'b'
Gibt es auch eine Möglichkeit,

Code: Alles auswählen

if s == 'a':
    print s
elif s == 'b':
    print 'b'
else:
    print 'c'
in gleicher Form zu schreiben? Die Documentation bzw. PEP 8 geben keine Auskunft darüber. Darum vermute ich, dass ich mit conditional expressions auf dem Holzweg bin. Vielleicht eher was mit 'or' bzw. 'and', aber irgendwie bin ich doch noch ein Anfänger... :(

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Oh Mann, sorry....

Code: Alles auswählen

print 'a' if s == 'a' else None or 'b' if s == 'b' else None or 'c' if s == 'c' else None
Oder geht's noch anderst?

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
BlackJack

@mutetella: Es geht auf jeden Fall mit "normalen" ``if``/``else`` *lesbar*. Ich verstehe den Ausdruck da jedenfalls nicht auf Anhieb weil ich mir erst einmal darüber klar werden müsste wo das per Default die Klammern stehen müssten.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@BlackJack:
Irgendwann und irgendwo hier im Forum wurde mir gesagt, dass 'return'-statements innerhalb von 'if'/'elif'/'else' nicht so schön seien. Und da ich in der Regel das tue, was man mir sagt :P , hab' ich eben versucht, das einzuhalten:

Code: Alles auswählen

    def date_is(self, date):
        return 'b' if self.is_base(date) else None or \
            'd' if self.is_duration(date) else None or \
            'r' if self.is_recurrence(date) else None
Ok, mein Beispiel war mit 'print'... sorry. Wie hältst Du das mit 'return'? Möglichst komplex oder auch mal verteilt über mehrere Abfragen?
Hmm..., zufällig bin ich gerade über

Simple is better than complex.
Complex is better than complicated.

aus dem 'Zen of Python' gestolpert. Passt in diesem Zusammenhang. Das sind halt auch immer so Sachen, die jeder ein wenig anderst sieht...

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
BlackJack

@mutetella: Das ist für mich einfach total unleserlich. Mittlerweile glaube ich zwar verstanden zu haben wie es funktioniert, bin mir aber nicht 100% sicher. Bei einem einfachen ``if``/``elif``/``else``-Konstrukt würde dagegen sogar ein Anfänger verstehen was da genau vorgeht.

Ich denke es sind implizite Klammern um die bedingten Ausdrücke und dadurch greift wegen der Semantik von ``or`` der Erste bei dem nicht `None` zurückgegeben wird. Was IMHO unsauber ist und auch fehleranfällig, weil man vor dem ``if`` nur "wahre" Ausdrücke stehen haben darf.

Ohne die ``or``\s könnte man das so schreiben:

Code: Alles auswählen

    def date_is(self, date):
        return 'b' if self.is_base(date) else (
            'd' if self.is_duration(date) else (
                'r' if self.is_recurrence(date) else None))
Aber wie gesagt, finde ich das verschachteln von bedingten Ausdrücken nur sehr bedingt lesbar. :-)

Ich würde vielleicht so etwas schreiben:

Code: Alles auswählen

    def date_is(self, date):
        for result, test_func in [('b', self.is_base),
                                  ('d', self.is_duration),
                                  ('r', self.is_recurrence)]:
            if test_func(date):
                return result
        return None
Ist IMHO einfacher zu lesen, einfacher erweiterbar, und erfüllt "flat is better than nested" aus dem Zen besser.

Aus OOP-Sicht könnte man sich auch fragen, ob so ein `date` nicht eventuell "seinen Buchstaben" kennen könnte.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

@BlackJack
Muss bei dir ja auch schon spät gewesen sein, ich erwähne nur mal ein Dictionary.
Und dann einfach "return dates.get(date, None)", oder liege ich da falsch?

Edit: Oha, ich hatte übersehen das du die Funktionen in deinem Beispiel schon rufst, aber hier mal ein Beispiel, wie ich mir das ungefähr vorstellen könnte:

Code: Alles auswählen

class Date(object):

    def __init__(self):
        self.dates = {"b" : self.is_base,
                      "d" : self.is_duration,
                      "r" : self.is_recurrence,
                      }

    def is_base(self):
        return 1

    def is_duration(self):
        return 2

    def is_recurrence(self):
        return 3

    def type_not_exist(self):
        raise TypeError("Dieser Datumstyp is unbekannt.")
    
    def date_is(self, date):
        return self.dates.get(date, self.type_not_exist)()


if __name__ == "__main__":
    d = Date()
    print(d.date_is("d"))
Natürlich müsste es bei den "is_"-Funktionen Booleans zurückgegeben werden, aber das soll das ganze nur deutlich machen.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

BlackJack hat geschrieben:Aus OOP-Sicht könnte man sich auch fragen, ob so ein `date` nicht eventuell "seinen Buchstaben" kennen könnte.
Eben das möchte ich mit dieser Methode erreichen. 'date_is' ist eine Methode von Entry() und soll zurückgeben, um was es sich bei einem date innerhalb des entrys handelt. Dem Entry()-Exemplar ist ja lediglich sein Beginn, seine Dauer und ein eventuelles Recurrence()-Exemplar bekannt.
Ein date-Objekt, das unter anderem seine Position ('b', 'd' oder 'r') kennt, kann es IMHO so nicht geben, da zu einem Datum mehrere entries existieren können.
Um ein Datum darzustellen, habe ich eine DaySheet()-Klasse gemacht, deren Exemplare alle zu einem Datum gehörenden entries sammeln.
Aber wie immer ich das alles strukturiere... irgendwo muss die Position ermittelt werden. Ich dachte auch schon daran, den entries ein Attribut 'position' zu spendieren, das bei einer 'is_match'-Abfrage, die ja base, duration und recurrence untersucht, auf das jeweilige Ergebnis gesetzt wird. Hat halt den 'Haken', dass dieses Attribut erst nach einer 'is_match'-Abfrage verlässliche Informationen liefert. 'is_match' müsste also sowohl bei der Erstellung wie auch jeder Attributänderung aufgerufen werden. Nicht so schön, oder?
Xynon1 hat geschrieben:... hier mal ein Beispiel, wie ich mir das ungefähr vorstellen könnte
Letztlich das gleiche mit einem dict. Da kommt mir, bevor ich das Problem überhaupt abgeschlossen habe, schon gleich wieder die Frage in den Sinn: Welche von beiden Möglichkeiten wird wohl die Schnellere sein... :mrgreen:

mutetella


EDIT: Das mit dem 'position'-Argument ist totaler Schwachsinn... Dadurch hätte das Entry()-Exemplar eine Information, die sich lediglich auf die letzte durchgeführte 'is_match'-Abfrage bezieht. "Wo" sich ein Entry()-Exemplar befindet, kann das Entry aus genannten Gründen erstmal nicht wissen, außer es würde im voraus alle date's ermitteln, an denen es auftritt. Und das ist IMHO nicht praktikabel.
Zuletzt geändert von mutetella am Sonntag 6. Februar 2011, 10:34, insgesamt 1-mal geändert.
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
BlackJack

@Xynon1: Das macht aber was anderes. Und was mir insgesamt nicht so gut gefällt bei allen Lösungen, ist das bei der API immer mehr `is_*()`-Methoden dazu kommen wenn mehr Typen dazukommen. Und bei Dir müsste man das alles auf dem `Date`-Basistyp machen. Also wenn ich einen neuen Typ hinzufügen will, müsste ich den Basistyp auch anfassen. Irgendwie unschön.

Und `is_*()`-Methoden sollten `True`/`False` zurückgeben und keine Zahlen oder sie sollten anders heissen.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

BlackJack hat geschrieben:Das macht aber was anderes.
Warum?
BlackJack hat geschrieben:... oder sie sollten anders heissen.
'date_is' hätte es auch nicht länger als unbedingt nötigt überlebt. Ich tendiere zu 'position'.

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

@BlackJack
Das mit den Booleans hatte ich doch noch unten mit hingeschrieben.
Und :oops:, stimmt macht was anderes, ich dachte es ging darum mit einem Kürzel zu prüfen, ob es ein bestimmter Typ ist, du machst es ja genau anders herum. Bei dir wird ja der Kürzel zurückgegeben.

Edit: Lag natürlich an der verwirrenden Bezeichnung "date_is" :) , hieß ja nicht "get_datetype".
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Xynon1 hat geschrieben:... ich dachte es ging darum mit einem Kürzel zu prüfen, ob es ein bestimmter Typ ist, du machst es ja genau anders herum. Bei dir wird ja der Kürzel zurückgegeben.
Ok, versteh' ich jetzt auch. Fiel mir beim 'Überfliegen' gar nicht auf... :oops: Liegt auch am IMHO unglücklich gewählten 'date' als Argumentname... :wink:

Ok, ich werde dann mal BlackJacks Vorschlag übernehmen...

Vielen Dank an Euch...

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

mutetella hat geschrieben:

Code: Alles auswählen

if s == 'a':
    print s
elif s == 'b':
    print 'b'
else:
    print 'c'
Daraus kann man als Conditional Expression sowas schreiben:

Code: Alles auswählen

print s if s in 'abc' else None
Oder ohne CE (weil hier unnötig):

Code: Alles auswählen

if s in 'abc': print s
Und mit Funktionen (ungetestet):

Code: Alles auswählen

DATE_STYLE_BASE, DATE_STYLE_DURATION, DATE_STYLE_RECURRENCE = range(3)

def get_date_style(self, date):
    style_checks = (self.is_base, self.is_duration, self.is_recurrence)
    for (style, style_check) in enumerate(style_checks):
        if style_check(date):
            return style
Letzteres hat den Nachteil, dass man auf die Reihenfolge der Checks achten muss, um die Werte nicht durcheinander zu bringen. Ich würde die Sachen ja einfach ausschreiben:

Code: Alles auswählen

DATE_STYLE_BASE, DATE_STYLE_DURATION, DATE_STYLE_RECURRENCE = range(3)

def get_date_style(self, date):
    if self.is_base(date):
        return DATE_STYLE_BASE
    elif self.is_duration(date):
        return DATE_STYLE_DURATION
    elif self.is_recurrence(date):
        return DATE_STYLE_RECURRENCE
    else:
        #return None
        raise ValueError('Unable to detect date style')
Da kann IMHO keiner sagen, dies sei unlesbar.
Antworten