2016-06-03 7 views
1

Beide Funktionen machen das Gleiche.Nested if's - was ist Pythonic?

def function1(self): 
    a = self.get_a() 
    b = self.get_b() 
    c = self.get_c() 
    r = None 

    if a: 
     r = a 
     if b: 
      r = b 
      if c: 
       r = c 
      else: 
       print("c not set.") 
     else: 
      print("b not set.") 
    else: 
     print("a not set.") 

    return r 



def function2(self): 
    a = self.get_a() 
    b = self.get_b() 
    c = self.get_c() 
    r = None 

    if not a: 
     print("a not set.") 
     return r 

    r = a 
    if not b: 
     print("b not set.") 
     return r 

    r = b 
    if not c: 
     print("c not set.") 

    r = c 
    return r 

function1() erzeugt sehr lange Schlangen mehr, wenn die verschachtelt sind, welche Linienlängenbegrenzung von 78

function2 mit PEP8 den Konflikten() könnte schwieriger sein, zu lesen/verstehen und mehr return-Anweisungen hat. Leitungslänge ist hier kein Problem.

Welches ist mehr Python?

+2

Sie können auch 'r' loswerden und direkt' a', 'b' oder' c' zurückgeben. – Selcuk

+0

Wenn Sie die 'print'-Aufrufe in den' else'-Zweigen nicht benötigen, können Sie die gesamte Berechnung auf einmal durchführen, mit 'a und (b und c oder b) oder a oder None'. Das 'oder None' am Ende ist nur dann notwendig, wenn die möglichen" falsey "-Werte, auf die Sie testen, nicht auf None beschränkt sind (und es ist Ihnen wichtig," None "anstatt einer anderen falschen Sache zurückzugeben). – Blckknght

+0

@Blckknght Ich habe deinen Kommentar nicht gesehen, bevor ich ihn eingereicht habe. Wenn du ihn als Antwort einreichen willst, werde ich meinen löschen. –

Antwort

4

Einer der Grundsätze des Pythonic-Codes ist "flach ist besser als verschachtelt". Auf dieser Grundlage werde ich sagen, function2() ist objektiv mehr Pythonic.

Der Zen von Python

Beautiful is better than ugly. 
Explicit is better than implicit. 
Simple is better than complex. 
Complex is better than complicated. 
Flat is better than nested. 
Sparse is better than dense. 
Readability counts. 
Special cases aren't special enough to break the rules. 
Although practicality beats purity. 
Errors should never pass silently. 
Unless explicitly silenced. 
In the face of ambiguity, refuse the temptation to guess. 
There should be one-- and preferably only one --obvious way to do it. 
Although that way may not be obvious at first unless you're Dutch. 
Now is better than never. 
Although never is often better than *right* now. 
If the implementation is hard to explain, it's a bad idea. 
If the implementation is easy to explain, it may be a good idea. 
Namespaces are one honking great idea -- let's do more of those! 

import this in dem Python-Interpreter durch Eingabe Dies kann gesehen werden: Dies kann in PEP-20: The Zen of Python zu sehen.

+3

Danke für die 'importieren Sie das'. Ich habe das noch nie gesehen – DomTomCat

+0

Ja, es ist ein cooles kleines Python Osterei :) – Will

2

Wie die Antwort von Will zeigt, ist Flat besser. Allerdings sieht der Code sowieso nicht sehr hübsch aus. Wie wäre es mit einer kompakteren Art von Code?

bei diesen Zitaten von @ Wills Antwort suchen:

Ablesbarkeit zählt.

Schön ist besser als hässlich.

from collections import OrderedDict 
def function3(): 
    my_dictionary=OrderedDict() 
    my_dictionary['a'] = self.get_a() 
    my_dictionary['b'] = self.get_b() 
    my_dictionary['c'] = self.get_c() 
    # ... 
    r = None 

    for name in my_dictionary.keys(): 
     value = my_dictionary[name] 
     if not value: 
      print("%s not set." % name) 
      return r 
     r = value 
    return r 

Sicher kann dies noch weiter verbessert werden

+1

Tolle Ergänzungen, danke! – Will

+0

Aber das Verhalten von Funktion_1, Funktion_2 und Funktion_3 sind alle paarweise unterschiedlich ;-) vgl. die Testergebnisse meiner vorgeschlagenen Antwort – Dilettant

+1

Oh, vergaß eine letzte "Rückkehr", um funct1 zu entsprechen. Ich denke, es geht um das Prinzip sowieso – DomTomCat

1

Sie können die Bewertungsregeln der and und or Operatoren verwenden, zum Beispiel:

>>> None or 4 or None or 5 
4 

>>> 4 and 5 
5 

So müssten Sie so etwas wie :

def function3(self): 
    a = self.get_a() 
    b = self.get_b() 
    c = self.get_c() 
    return (a and b and c) or (a and b) or a or None 

Und ich würde empfehlen, I/O von Ihrem logischen Code auszuschließen.

+0

Lesbarkeit ist wichtig und Erweiterbarkeit - wenn es 3 Anfrage Dimensionen und eine Standardeinstellung für immer bleiben würde, würde die kombinierte logische online, die Sie vorschlagen, mein Favorit sein, aber auf meinem Planeten in den letzten Jahrzehnten fast immer benötigte Erweiterung von Dimensionen wie ein anderer Sensor, ein anderes Attribut, und das schlägt vor, eine Bestellung früher oder später zu deklarieren, passende Container auszuwählen und dann über diese zu iterieren ... – Dilettant

+0

@Dilettant Ich würde denken, dass es ziemlich einfach wäre, dies zu erweitern ein allgemeiner Fall mit etwas Generator-Ausdruck-Magie. Wie: 'reduzieren (Lambda a, b: a oder b, [reduzieren (Lambda x, y: x und y, vals [: i]) für i im Bereich (1, len (vals) +1)], None) 'wo' vals' die Liste der Werte enthält, an denen du interessiert bist. Oder so ähnlich, ich habe es noch nicht gründlich durchdacht. –

+0

Es gab einen Grund, warum verschwinden von den eingebauten in Python3 verschwinden ;-) im Zusammenhang mit den Beispielen auf dieser Seite, die alle funktional gleichwertig waren und meistens nicht waren, ist klar, wie leicht verschachtelt (tief verschachtelte Funktion_1), copy-paste-like (Funktion_2), im Wesentlichen Einzeiler (Ihr Vorschlag) sind schwer zu vergleichen, stellen Sie sicher, dass alle gleich sind und alle zusätzliches Gewicht in Wartung tragen ... und ich verstand das OP zu Beginn der Reise zu Python-Meisterschaft. – Dilettant

0

Ich schlage vor, function_4 unten zusammen mit den Fragen angezeigt (nicht idetnically arbeiten!) Funktionen und einer von DomTomCat Antwort:

#! /usr/bin/env python 
from __future__ import print_function 
from collections import OrderedDict # Only used in function_3 


def function_4(self): 
    """Iterate over call results in FIFO on False or if sequence 
    exhausted, return None or previous value if that evaluates to true.""" 

    functors = (
     self.get_a, 
     self.get_b, 
     self.get_c, 
    ) 
    request_targets = (
     'a', 
     'b', 
     'c', 
    ) 
    response_value = None 
    for functor, request_target in zip(functors, request_targets): 
     current_response = functor() 
     if not current_response: 
      print(request_target, "not set.") 
      return response_value 
     else: 
      response_value = current_response 

    return response_value 


class Foo(object): 
    """Mock the thingy ...""" 
    def __init__(self, a, b, c): 
     self._a, self._b, self._c = a, b, c 

    def __repr__(self): 
     return (
      "Foo(" + str(self._a) + ", " + str(self._b) + ", " + 
      str(self._c) + ")") 

    def get_a(self): 
     return self._a 

    def get_b(self): 
     return self._b 

    def get_c(self): 
     return self._c 


def function_1(self): 
    a = self.get_a() 
    b = self.get_b() 
    c = self.get_c() 
    r = None 

    if a: 
     r = a 
     if b: 
      r = b 
      if c: 
       r = c 
      else: 
       print("c not set.") 
     else: 
      print("b not set.") 
    else: 
     print("a not set.") 

    return r 


def function_2(self): 
    a = self.get_a() 
    b = self.get_b() 
    c = self.get_c() 
    r = None 

    if not a: 
     print("a not set.") 
     return r 

    r = a 
    if not b: 
     print("b not set.") 
     return r 

    r = b 
    if not c: 
     print("c not set.") 

    r = c 
    return r 


def function_3(self): 
    my_dictionary = OrderedDict() 
    my_dictionary['a'] = self.get_a() 
    my_dictionary['b'] = self.get_b() 
    my_dictionary['c'] = self.get_c() 
    # ... 
    r = None 

    for name in my_dictionary.keys(): 
     value = my_dictionary[name] 
     if not value: 
      print("%s not set." % name) 
      return r 
     r = value 


def main(): 
    """"Drive the investigation.""" 
    fixtures = (
     (1, 42, 3.1415), 
     (0, 42, 3.1415), 
     (1, 0, 3.1415), 
     (1, 42, 0), 
    ) 
    functors = (
     function_1, 
     function_2, 
     function_3, 
     function_4, 
    ) 
    for fixture in fixtures: 
     foo = Foo(*fixture) 
     print("\nFixture:", foo) 
     for i, functor in enumerate(functors, start=1): 
      print("Functor[%d]:" % (i,)) 
      print(functor(foo)) 


if __name__ == '__main__': 
    main() 

Auf meinem Rechner die Leuchten erzeugen das folgende Verhalten/Ausgang, wenn sie aufgerufen werden:

Fixture: Foo(1, 42, 3.1415) 
Functor[1]: 
3.1415 
Functor[2]: 
3.1415 
Functor[3]: 
None 
Functor[4]: 
3.1415 

Fixture: Foo(0, 42, 3.1415) 
Functor[1]: 
a not set. 
None 
Functor[2]: 
a not set. 
None 
Functor[3]: 
a not set. 
None 
Functor[4]: 
a not set. 
None 

Fixture: Foo(1, 0, 3.1415) 
Functor[1]: 
b not set. 
1 
Functor[2]: 
b not set. 
1 
Functor[3]: 
b not set. 
1 
Functor[4]: 
b not set. 
1 

Fixture: Foo(1, 42, 0) 
Functor[1]: 
c not set. 
42 
Functor[2]: 
c not set. 
0 
Functor[3]: 
c not set. 
42 
Functor[4]: 
c not set. 
42 
[Finished in 0.0s]