嵌套 if - 还有什么更 Pythonic 的?

Nested if's - what's more 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() 创建的行越长,嵌套的 if 越多,这与 PEP8 的行长度限制 78 相冲突。

function2() 可能更难 read/understand 并且有更多 return 语句。这里的行长没有问题。

哪个更像 pythonic?

Pythonic 代码的主要原则之一是 "flat is better than nested"。在此基础上,我会说 function2() 客观上更 Pythonic。这可以在 PEP-20: The Zen of Python:

中看到

The Zen of 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!

这可以通过在 Python 解释器中键入 import this 来查看。

正如@Will 的回答所暗示的那样,平坦更好。然而,代码看起来不是很漂亮。更紧凑的代码类型如何?

查看@Will 的回答中的这些引用:

Readability counts.

Beautiful is better than ugly.

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

当然可以进一步改进

可以使用andor运算符的求值规则,例如:

>>> None or 4 or None or 5
4

>>> 4 and 5
5

所以你会有这样的东西:

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

而且我建议从您的逻辑代码中分解出您 I/O。

我建议在下面显示 function_4 以及问题(非相同工作!)功能和 DomTomCat 的回答之一:

#! /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()

在我的机器上,固定装置在被调用时产生以下 behaviour/output:

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]

这是我在不删除打印语句的情况下会做的事情

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

    inputValues = [a, b, c]
    setValues = [i for i in inputValues if i]
    for index, value in inputValues:
        if len(setValues) <= index or setValues[index] != value:
            print(f'{value} is not set.')
        else:
            r = value
    return r

function2看起来不错,可以走了。