2014-01-09 15 views
9

Ich versuche, die Kompromisse zwischen verschiedenen Ansätzen zu ermitteln, ob mit Objekt obj Aktion do_stuff() durchgeführt werden kann. Wie ich es verstehe, gibt es drei Möglichkeiten, um zu bestimmen, ob dies möglich ist:python isinstance vs hasattr vs versuchen/außer: Was ist besser?

Welche ist die bevorzugte Methode (und warum)?

+1

Es gibt Zeiten für alles. Normalerweise versuchen/außer (oder nicht einmal "außer", lassen Sie einfach die Ausnahme perculate den Call-Stack) ist der Weg zu gehen, aber es gibt Ausnahmen (hohoho) für jede Regel. Zum Beispiel teilen Strings und Listen viele Methoden, aber Sie möchten in einem rekursiven Aufruf oft verschiedene Dinge mit ihnen machen. – roippi

Antwort

6

Ich glaube, dass die letzte Methode im Allgemeinen von Python-Kodierern wegen einer gelehrt wird, die in der Python-Gemeinschaft gelehrt wird: "Leichter um Vergebung bitten als Erlaubnis" (EAFP).

Kurz gesagt bedeutet das Motto zu vermeiden, zu prüfen, ob Sie etwas tun können, bevor Sie es tun. Führen Sie stattdessen einfach die Operation aus. Wenn es fehlschlägt, behandeln Sie es entsprechend.

Auch die dritte Methode hat den zusätzlichen Vorteil, dass es klar ist, dass die Operation funktionieren sollte.


das gesagt ist, sollten Sie wirklich mit einem nackten except wie die Verwendung vermeiden. Wenn Sie dies tun, werden alle Ausnahmen erfasst, auch die nicht verwandten Ausnahmen. Stattdessen sollten Sie Ausnahmen am besten erfassen.

Hier finden Sie eine AttributeError erfassen möchten:

try: 
    obj.do_stuff() # Try to invoke do_stuff 
except AttributeError: 
    print 'Do something else' # If unsuccessful, do something else 
+0

Gibt es einen Vorteil, wenn Sie versuchen, auf das Methodenattribut zuzugreifen, und es dann in der "else" -Klausel aufzurufen, anstatt es einfach in der "try" -Klausel aufzurufen? (Ich nehme an, Sie möchten vielleicht zwischen einem 'AttributeError' unterscheiden, der in' do_stuff' ausgelöst wird, und einem, der von 'do_stuff' erzeugt wird.) – chepner

+0

@chepner - Nein, guter Haken. Ich dachte an etwas anderes. – iCodez

5

Überprüfung mit isinstance läuft entgegen der Python Konvention duck typing zu verwenden.

hasattr funktioniert gut, aber ist Look Before you Leap anstelle der mehr Pythonic EAFP.

Ihre Implementierung von Weg 3 ist gefährlich, da sie alle Fehler erfasst, einschließlich derjenigen, die durch die do_stuff Methode hervorgerufen werden. Sie könnten mit der präziseren gehen:

try: 
    _ds = obj.do_stuff 
except AttributeError: 
    print('Do something else') 
else: 
    _ds() 

Aber in diesem Fall würde ich Art und Weise 2 trotz des leichten Kopf bevorzugen - es ist einfach viel besser lesbar.

+0

Ich vermute, du meintest "hasattr funktioniert gut, aber ist EAFP anstelle der mehr Pythonic LBYL." umgekehrt zu lesen "hasattr funktioniert gut, aber ist LBYL statt der mehr Pythonic EAFP". – mloskot

+1

@mloskot Danke, behoben. – phihag

3

Die richtige Antwort ist 'weder' hasattr bietet Funktionalität, aber es ist möglicherweise die schlechteste aller Optionen.

Wir verwenden die objektorientierte Natur von Python, weil es funktioniert. OO-Analyse ist nie genau und oft verwirrt, aber wir verwenden Klassenhierarchien, weil wir wissen, dass sie Menschen helfen, schneller zu arbeiten. Menschen erfassen Objekte und ein gutes Objektmodell hilft Programmierern, Dinge schneller und mit weniger Fehlern zu ändern. Der richtige Code endet an den richtigen Stellen gruppiert.Die Objekte:

  • Kann nur ohne vorhanden ist, die Umsetzung unter Berücksichtigung verwendet werden
  • deutlich machen, was geändert werden muss und wo
  • Isolate Änderungen einiger Funktionalität von Änderungen auf eine andere Funktionalität - Sie können beheben X ohne zu fürchten, werden Sie Y brechen

hasattr vs isinstance

Having Die Verwendung von "isinstance" oder "hasattr" deutet darauf hin, dass das Objektmodell fehlerhaft ist oder wir es falsch verwenden. Das Richtige ist, das Objektmodell zu reparieren oder zu ändern, wie wir es verwenden. Diese beiden Konstrukte haben die gleiche Wirkung und im Imperativ "Ich brauche den Code, um dies zu tun" sind sie gleichwertig. Strukturell gibt es einen großen Unterschied. Wenn man diese Methode zum ersten Mal (oder nach einigen Monaten anderer Dinge) trifft, vermittelt isinstance einen Reichtum mehr Informationen darüber, was tatsächlich vor sich geht und was sonst noch möglich ist. Hasattr sagt dir nichts.

Eine lange Geschichte der Entwicklung führte uns weg von FORTRAN und Code mit vielen 'Wer bin ich' Schaltern. Wir entscheiden uns dafür, Objekte zu verwenden, da wir wissen, dass sie die Arbeit mit dem Code erleichtern. Wenn wir hasattr wählen, liefern wir Funktionalität, aber nichts ist behoben, der Code ist kaputter als vor dem Start. Wenn wir diese Funktionalität in der Zukunft hinzufügen oder ändern, müssen wir uns mit Code beschäftigen, der ungleich gruppiert ist und mindestens zwei Organisationsprinzipien hat, von denen einige dort sind, wo sie sein sollten, und der Rest zufällig an anderen Orten verteilt ist. Es gibt nichts, was es zusammenhält. Dies ist nicht ein Fehler, sondern ein Minenfeld von möglichen Fehlern, die über jeden Ausführungspfad verstreut sind, der durch Ihren Hasattr passiert.

Also, wenn es eine Wahl ist, ist die Reihenfolge:

  1. Verwenden Sie das Objektmodell oder es beheben oder zumindest herausfinden, was mit ihm falsch ist und wie man es beheben
  2. Verwendung isinstance
  3. Verwenden Sie nicht hasattr
+0

Was meinst du mit "Fixieren des Objektmodells", in diesem Fall? Und was nützt "isinstance" hier? Sie können beispielsweise prüfen, ob "object" eine Datenstruktur mit einem Shape-Attribut ist, und das wäre ein Problem, wenn Sie alle Strukturen für die Übergabe an "isinstance" a priori auflisten müssten. Was halten Sie schließlich von "getattr" und "try/except", die von einigen als gute Lösungen angesehen wurden? –