Vereinfachung immerwiederkehrender Ausdruck

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.
rayo
User
Beiträge: 773
Registriert: Mittwoch 5. November 2003, 18:06
Wohnort: Schweiz
Kontaktdaten:

Beitragvon rayo » Dienstag 26. Dezember 2006, 13:27

Hi

Deine Funktion sollte nicht False zurückgeben bei Fehlern, sondern entweder die Exception einfach weitergeben (nichts machen) oder die Exception von os.listdir abfangen mit try except und eine eigene Exception auslösen und diese dann ausserhalb abfangen.

Code: Alles auswählen

class MeineException(Exception):
    pass
def list_dir(path):
    try:
        c = os.listdir(path)
    except Exception,e:
        raise MeineException("Fehler bei listdir")
    r = {
        'dirs': [],
        'files': [],
        'links': []
    }
    for i in c:
        item = os.path.join(path, i)
        if os.is_link(item):
            r['links'].append(i)
        elif os.is_dir(item):
            r['dirs'].append(i)
        elif os.is_file(item):
            r['files'].append(i)
    return r
try:
    a = list_dir(mein_pfad)
    print a
except MeineException, e:
    print 'listdir fehlgeschlagen'


So finde ich das am schönsten. Natürlich solltest du noch einen besseren Namen für die Exception ausdenken.

Gruss

PS: für den rekursiven durchlauf von Verzeichnissen ist os.walk ganz gut geeignet ;)
BlackJack

Re: Vereinfachung immerwiederkehrender Ausdruck

Beitragvon BlackJack » Dienstag 26. Dezember 2006, 14:41

droptix hat geschrieben:In einem Fall habe ich ne Funktion ListDir, die mir den Verzeichnisinhalt eines Pfades einliest. Wenn alles klar ging, liefert die Funktion ein Dict zurück. Wenn dabei was schief ging (Pfad falsch, keine Zugriffsrechte etc.), dann eben False:


Ich schliesse mich rayo an: Ausnahmen wurden eingeführt um spezielle Rückgabewerte, die einen Fehler signalisieren, loszuwerden. Sie zu behandeln indem ein "Fehlerwert" zurückgegeben wird, ist ein Rückschritt. Behandele die Ausnahme lieber in Deiner `walk()`-Funktion.

Aber werde auf jeden Fall den Identitätstest auf `False` los. `True` und `False` sind nur Namen, es handelt sich nicht um Singletons wie bei `None`, also sollte man auch nicht auf Identität sondern auf Gleichheit testen.

Ich hätte die Funtkionen so geschrieben:

Code: Alles auswählen

def list_dir(path):
    dirs = list()
    files = list()
    links = list()
    for name in os.listdir(path):
        full_name = os.path.join(path, name)
        if os.path.islink(full_name):
            links.append(name)
        elif os.path.isdir(full_name):
            dirs.append(name)
        elif os.path.isfile(full_name):
            files.append(name)
    return dirs, files, links


def walk(path):
    try:
        dirs, dummy, dummy = list_dir(path)
        print path
        for dirname in dirs:
            walk(os.path.join(path, dirname))
    except IOError:
        pass


Wenn man etwas mehr Kontrolle über die Fehlerbehandlung in `walk()` haben möchte, kann man auch eine "callback"-Funktion einführen, die entscheidet was im Fehlerfall passieren soll:

Code: Alles auswählen

def walk(path, onerror=None):
    try:
        dirs, dummy, dummy = list_dir(path)
        print path
        for dirname in dirs:
            walk(os.path.join(path, dirname))
    except IOError, error:
        if onerror is not None:
            if onerror == 'raise':
                raise
            else:
                onerror(error)
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Re: Vereinfachung immerwiederkehrender Ausdruck

Beitragvon sape » Dienstag 26. Dezember 2006, 16:45

droptix hat geschrieben:[...]
Macht bitte keine Wissenschaft draus... mit foo() meinte ich eine simple Funktion und mit Prozedur die Aufgabe, die die Funktion zu erfüllen hat. Wenn die Aufgabe fehlschlägt (z.B. Division durch Null oder was weiß ich sonst noch), dann liefert die Funktion halt False zurück.
Du hast von einer Methode innerhalb von `foo`geredet und da bin ich dann davon ausgegangen das `foo` eine Klasse sein muss, weil alle Funktionen innerhalb einer Klasse eben Methode genannt werden. Daher meine frage. Also Sorry, war mein Fehler...

In einem Fall habe ich ne Funktion ListDir, die mir den Verzeichnisinhalt eines Pfades einliest. Wenn alles klar ging, liefert die Funktion ein Dict zurück. Wenn dabei was schief ging (Pfad falsch, keine Zugriffsrechte etc.), dann eben False:
Da würde ich eine Exception liefern! Grade bei solchen Sachen wie "Datei nicht vorhanden, Falscher Pfad, ungültige Dateizugriffsrechte" _müssen_ Exceptions (die darauf hinweißen) ausgelöst werden. Schließlich ist das ja eine Ausnahme verhalten. Ein schlichtes `False` ist einfach ungenügend und man weiß dann nicht woran der Fehler lag und kann dann nicht darauf entsprechend richtig reagieren.

Versuch deine Sachen so zu schreiben das man die auch unabhängig von deinem Programm benutzen könnte und denke dann dabei was du gerne erwarten würdest, von einer nicht von dir geschriebenen Klasse/Funktion, wenn eine Sache nicht richtig ist? -> Würdest du lieber ein `False` wollen Für 3 Unterschiedliche Fehler oder lieber eine Konkrete Exception (jeweile eine eigene für unterschiedliche Fehler), mit dessem Auslösen du dann jeweils drauf entsprechend reagieren kannst?

Davon abgesehen bietet es sich an `True`und `False`nur bei `is_xyz` als Rückgabewert zu benutzen. Es gibt kaum ncoh adere Sinnvolle Sachen wo sich das anbietet würde. (Meiner Meinung nach).

Und wie BlackJack schon schrieb, wurde das Exceptionhandling (ich glaube C++ war so mit der ersten Sprachen die das angeboten hat) eingeführt, damit wir von dieser lästigen Fehlercode Überprüfung verschont werden.

lg
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Beitragvon droptix » Mittwoch 27. Dezember 2006, 11:44

rayo hat geschrieben:PS: für den rekursiven durchlauf von Verzeichnissen ist os.walk ganz gut geeignet ;)


In meinem Fall ungenügend, weil ich auch Symlinks auswerten möchte und später auch Junctions.

BlackJack hat geschrieben:Ich schliesse mich rayo an: Ausnahmen wurden eingeführt um spezielle Rückgabewerte, die einen Fehler signalisieren, loszuwerden. Sie zu behandeln indem ein "Fehlerwert" zurückgegeben wird, ist ein Rückschritt.


Hmmm, manchmal ist das einfach unangenehm, viele try-except-Statements zu verwenden, wenn mir ein einfaches False reichen würde. Denn ich möchte gar nicht weiter auswerten, warum os.listdir() eine Ausnahme erzeugte. Wenn ein Fehler vorkommt, wird das aktuelle Verzeichnis eben übersprungen.

BlackJack hat geschrieben:Aber werde auf jeden Fall den Identitätstest auf `False` los. `True` und `False` sind nur Namen, es handelt sich nicht um Singletons wie bei `None`, also sollte man auch nicht auf Identität sondern auf Gleichheit testen.


Uh, was ist der genaue Unterschied zwischen Gleicheit und Identität? Und was ist ein Singleton?

BlackJack hat geschrieben:Ich hätte die Funtkionen so geschrieben:

Code: Alles auswählen

def list_dir(path):
    dirs = list()
    files = list()
    links = list()
    for name in os.listdir(path):
        full_name = os.path.join(path, name)
        if os.path.islink(full_name):
            links.append(name)
        elif os.path.isdir(full_name):
            dirs.append(name)
        elif os.path.isfile(full_name):
            files.append(name)
    return dirs, files, links


def walk(path):
    try:
        dirs, dummy, dummy = list_dir(path)
        print path
        for dirname in dirs:
            walk(os.path.join(path, dirname))
    except IOError:
        pass


Ist netter, geb ich zu. Bei except passiert hier halt nix. Aber es wäre zumindest vorbereitet, wenn das später mal interessant werden sollte... :) Macht es hier eigentlich einen Unterschied, ob man foo = [] oder foo = list() verwendet?

sape hat geschrieben:Du hast von einer Methode innerhalb von `foo`geredet und da bin ich dann davon ausgegangen das `foo` eine Klasse sein muss, weil alle Funktionen innerhalb einer Klasse eben Methode genannt werden.


Nee, hab ich nie so erwähnt. Daher verwunderte mich deine Interpretationsfreude :D
Benutzeravatar
Leonidas
Administrator
Beiträge: 16023
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Beitragvon Leonidas » Mittwoch 27. Dezember 2006, 12:16

droptix hat geschrieben:Macht es hier eigentlich einen Unterschied, ob man foo = [] oder foo = list() verwendet?

Nein, im Großen und Ganzen eigentlich nicht. Aber guck doch mal in die [wiki]FAQ#GibtEsEinenUnterschiedZwischenlistBzwdict[/wiki], dort wird genau diese Frage diskutiert.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
BlackJack

Beitragvon BlackJack » Mittwoch 27. Dezember 2006, 13:20

droptix hat geschrieben:Uh, was ist der genaue Unterschied zwischen Gleicheit und Identität?


Das ist der Unterschied zwischen "das selbe" und "das gleiche", den es laut Duden im deutschen leider nicht mehr gibt. :-(

Mit ``is`` testet man, ob es sich tatsächlich um das selbe Objekt handelt. Es gibt also nur ein einziges Objekt, das diese Eigenschaft erfüllt. Mit ``==`` testet man auf Gleichheit, was nicht so scharf definiert ist, bzw. in Python vom Programmierer festgelegt werden kann. Beispiel:

Code: Alles auswählen

In [38]: a = 1

In [39]: b = 1.0

In [40]: type(a), type(b)
Out[40]: (<type 'int'>, <type 'float'>)

In [41]: a is b
Out[41]: False

In [42]: a == b
Out[42]: True


`a` und `b` sind ganz offensichtlich zwei verschiedene Objekte, sogar von unterschiedlichem Typ, aber der Wert ist gleich.

`True` und `False` gab es früher in Python nicht, darum haben sich viele Leute in ihren Modulen die Namen `True` und `False` an "wahre"/"falsche" Werte gebunden, z.B. 1 und 0, um den Quelltext lesbarer zu machen. Deshalb kann es verschiedene `True`-Objekte in einem Programm geben, die mit ``is`` verglichen, "falsch" ergeben aber mit ``==`` "wahr" sind.

In [43]: 1 is True
Out[43]: False

In [44]: 1 == True
Out[44]: True


Auf Identität sollte man wirklich nur prüfen, wenn man exakt ein ganz bestimmtes Objekt erwartet. Das ist bei `None` gegeben, weil `None` ein Singleton ist, also ein Objekt von dem es nur ein Exemplar gibt, und der Name `None` auch nicht an andere Objekte gebunden werden kann:

Code: Alles auswählen

In [45]: None = "hallo"
------------------------------------------------------------
SyntaxError: assignment to None


Ansonsten kann man ``is`` noch in Graphenalgorithmen verwenden um zum Beispiel festzustellen, ob man bei einem Objekt, und genau dem Objekt, schon einmal vorbeigekommen ist, oder um bei Algorithmen den Knoten auf dem die Methode ausgeführt wird auszuschliessen, indem man auf ``is not self`` testet.

Eine weitere Einsatzmöglichkeit sind "Sentinel"-Objekte, deren einziger Zweck es ist, in einem Identitätsvergleich verwendet zu werden. Zum Beispiel wenn man eine Funktion schreibt, die ein optionales Argument erhält, welches wirklich jeden Wert annehmen darf, also auch `False`, `None` und so weiter, aber nur berücksichtigt werden soll, wenn es beim Aufruf angegeben wurde. Das kann man z.B. so lösen:

Code: Alles auswählen

NOTHING = object()  # Absolut einzigartiges Objekt

def spam(eggs, opt=NOTHING):
    # ...
    if opt is not NOTHING:
        # ...
    # ...
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Zurück zum Ursprungsthema

Beitragvon droptix » Freitag 2. März 2007, 02:27

Zurück zum Ursprungsthema! Mir ist wieder eingefallen, wo ich diese PHP-ähnliche Syntax gern hätte bzw. wo sie wirklich Sinn machen würde, weil sie die Lesbarkeit des Codes verbessert. Vielleicht habt ihr aber auch wieder eine alternative Lösung… ich bin gespannt:

1) Momentan mache ich folgendes:

Code: Alles auswählen

x="blah"
f=foo(x)
if f:
   print f
else:
   b=bar(x)
   if b:
      print b
   else:
      print "strange"

Ich prüfe also zuerst, ob das return von `foo(x)` einen Wert liefert. Wenn dem so ist, das Ergebnis "ausdrucken". Wenn nicht, muss ich weiterhin prüfen, ob dann vielleicht `bar(x)` einen Wert liefert. Wenn das auch nicht der Fall ist, dann entspricht `x` nicht meinen Erwartungen für die Weiterverarbeitung.

Man könnte das Ganze auch so schreiben:

Code: Alles auswählen

x="blah"
f=foo(x)
b=bar(x)
if f:
   print f
elif b:
   print b
else:
   print "strange"

Aber: Wenn `bar()` eine sehr lange Durchlaufzeit hat und ich schon nach `if f:` ein sinnvolles Ergebnis erhalten würde, hätte ich mir den Durchlauf von `bar(x)` schenken können. Das mal noch in einer Schleife vorgestellt kann extreme Wartezeiten produzieren.

2) Nach der PHP-Methode müsste es in Python-Code ungefähr so aussehen:

Code: Alles auswählen

x="blah"
if f=foo(x):
   print f
elif b=bar(x):
   print b
else:
   print "strange"

Also gleich im `if`-Statement kann der Rückgabewert aufgefangen werden, um ihn anschließend weiter zu verarbeiten.

Wenn sich das jetzt nicht nur auf 2 Funktionen beschränkt, die `x` überprüfen sollen, wird der Python-Code (s. erstes Snippet) extrem lang und sehr verschachtelt. Der PHP-Code bleibt dagegen übersichtlich.

Was meint ihr? Gibt's eine Alternative für meinen Wunsch in Python?
BlackJack

Beitragvon BlackJack » Freitag 2. März 2007, 08:11

"Gehe eine Reihe von Funktionen durch, bis eine davon mit dem `x` etwas brauchbares liefert" klingt sehr nach einer Schleife:

Code: Alles auswählen

    for func in (foo, bar, baz):
        result = func(x)
        if result:
            print result
            break
    else:
        print "strange"


Die Liste der Funktionen lässt sich einfach an genau einer Stelle erweitern.
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Beitragvon droptix » Freitag 2. März 2007, 10:17

Geile Idee! :D

Nachschlag: Wenn ich nun aber nicht nur `print x` brauche sondern in jedem Fall eine individuelle Auswertung benötige, was dann? Eine Schleife geht dann nicht mehr. Übertragen meine ich sowas:

Code: Alles auswählen

x="lala"
if f=foo(x):
    return blah(f)
elif b=bar(x):
    return blubb(b)
else:
    return None
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Selbstantwort

Beitragvon droptix » Freitag 2. März 2007, 10:29

Hm, zur Not vielleicht so hier:

Code: Alles auswählen

ok = False
for func in (foo, bar):
    result = func(x)
    if result:
        ok = True
        break

if ok:
    if func == foo:
        return blah(result)
    elif func == bar:
        return blubb(result)
    return False
else:
    return False

Das sieht aber auch unschön aus und man verwurstet die Funktionsnamen doppelt: einmal in der Schleife und dann unten in der `if`-Auswertung.
rayo
User
Beiträge: 773
Registriert: Mittwoch 5. November 2003, 18:06
Wohnort: Schweiz
Kontaktdaten:

Beitragvon rayo » Freitag 2. März 2007, 11:20

Hi

So siehts schöner aus

Code: Alles auswählen

functions = [(foo, blah), (bar, blubb)]

for first,second in functions:
    d = first(x)
    if d:
        return second(d)


Gruss
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Beitragvon droptix » Freitag 2. März 2007, 11:42

Ach ihr wieder :wink: Ich hatte sowas schon geahnt.

Ich wollte es wieder mal extra knapp halten, aber ich meinte es eigentlich so, dass jeder Fall eine individuelle Auswertung benötigt, die man nicht wieder unbedingt in eine Funktion packen muss. Zwei Zeilen Code pro Fall könnten da schon reichen, was eine Extra-Funktion unnötig macht. Also hier mal ein sinnvolleres Beispiel:

Code: Alles auswählen

# who knows what code happened before...

res = "strange"
x = "lala"
if r = foo(x):
   # this result could be a string
   res = "result is a foo: %s" % r
elif r = bar(x):
   # maybe this result is a number
   res = "result is a percent value: %s%%" % constantDefinedBefore * r / 100
elif r = baz(x):
   # or result is a special object
   res = "result is an object having the name %s and the following properties:" % r.name
   res += r.list_properties()
print res
BlackJack

Beitragvon BlackJack » Freitag 2. März 2007, 21:11

Schreiben muss man den Code so oder so und ein zusätzliches ``lambda x:`` ist IMHO vertretbar. Oder eben kleine (lokale) Funktionen für etwas komplexere Fälle.
Benutzeravatar
nkoehring
User
Beiträge: 543
Registriert: Mittwoch 7. Februar 2007, 17:37
Wohnort: naehe Halle/Saale
Kontaktdaten:

Beitragvon nkoehring » Samstag 3. März 2007, 08:13

Leonidas hat geschrieben:
droptix hat geschrieben:Macht es hier eigentlich einen Unterschied, ob man foo = [] oder foo = list() verwendet?

Nein, im Großen und Ganzen eigentlich nicht. Aber guck doch mal in die [wiki]FAQ#GibtEsEinenUnterschiedZwischenlistBzwdict[/wiki], dort wird genau diese Frage diskutiert.
Ich verwende der Lesbarkeit meistens list() und dict() ... falls das fuer dich eine Rolle spielen sollte :)

Ansonsten hab ich gerade keine Lust gehabt das alles zu lesen und ueberflog es nur mal schnell.

Kann es vielleicht sein, dass logische Operatoren vielleicht was mit dem zu tun haben, was du suchst (ich hoffe, ich steuere jetzt nicht and der Threadentwicklung vorbei :roll: )?

Code: Alles auswählen

a=foo()
if a: print a

ginge auch so hier:

Code: Alles auswählen

a=foo()
print a or "a is false"

oder so hier:

Code: Alles auswählen

a = foo() or "a is false"
print a


...ich hab das jetzt nicht extra probiert... aber normalerweise mache ich das oefter so ;)
Bitte kritisiert mich, wenn das als "unpythonisch", "unlesbar", "schlicht und einfach bloed" oder sowas gilt... dann beruecksichtige ich das vielleicht in Zukunft 8)
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Beitragvon droptix » Samstag 3. März 2007, 19:49

nkoehring hat geschrieben:(ich hoffe, ich steuere jetzt nicht and der Threadentwicklung vorbei :roll: )?


Du triffst es zwar nicht ganz, aber das bringt mich grad auf eine Idee:

Code: Alles auswählen

r = foo(x) or bar(x) or baz(x) or None

Geht das? Es ist zwar trotzdem nicht ganz brauchbar für meine Zwecke, aber eine coole Idee wie ich finde. Ich kann damit immernoch nicht individuell auf eine Aktion reagieren, da ich am Ende nicht weiß, welche Funktion in der OR-Verknüpfung einen gültigen Rückgabewert erzeugt hat.

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder