python if/elif/else,for和lists

Python If/Elif/Else, For and Lists

我尝试在列表上迭代elif语句,并在末尾使用else语句。这是我的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    if clickPoint is None:
        print(clickPoint)
    for each in meal_objects:
        if inside(clickPoint, each._button):
            each._button.setFill('green')
            break
    for each in build_meal_objects:
        if inside(clickPoint, each._button):
            each._button.setFill('green')
            break
    for each in ingredient_objects:
        if inside(clickPoint, each._button):
            each._button.setFill('green')
            break
    else:
        print(clickPoint)

食物对象、构建食物对象和成分对象都是列表。

问题是,由于许多原因,这个代码是可怕的。如果不满足第一个条件,则每个for循环都将运行,即使其中一个for循环条件已经运行,并且最终的else语句也将运行。实际上,如果满足任何for循环中的任何if语句,则不应执行其余的if块。

代码应该更像这个伪代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    if clickPoint is None:
        print(clickPoint)
    elif for each in meal_objects:
        if inside(clickPoint, each._button):
            each._button.setFill('green')
            break
    elif for each in build_meal_objects:
        if inside(clickPoint, each._button):
            each._button.setFill('green')
            break
    elif for each in ingredient_objects:
        if inside(clickPoint, each._button):
            each._button.setFill('green')
            break
    else:
        print(clickPoint)

我觉得我可能忽略了一些非常简单的事情,所以如果是这样,或者这是一个写得不好的问题,请原谅我。谢谢!


好吧,这需要一些解释。看看下面这个。

1
2
3
4
5
6
7
8
for x in range(5):
    for y in range(5):
        print (x*y)
        if x*y==3:
            break
    else:
        continue  # executed if the loop finished normally (no break)
    break         # executed if 'continue' was skipped (break)

上面的程序只打印值,直到找到一个3

输出:

1
2
3
4
5
6
7
8
9
0
0
0
0
0
0
1
2
3

如果最后一个continueelse没有出现怎么办?

1
2
3
4
5
for x in range(5):
    for y in range(5):
        print (x*y)
        if x*y==3:
            break

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
0
0
0
0
0
0
1
2
3
0
2
4
6
8
0
3
0
4
8
12
16

看,它甚至在找到一个3后都没有停止,这是因为break只导致退出内环。因此,可以使用第一个代码退出嵌套循环。即使是非常深的嵌套循环。

那么如何在代码中应用它呢?看这个!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
my_objects = {0: meal_objects, 1: build_meal_objects, 2: ingredient_objects}

flag2=True
flag1=True
if clickPoint is None:
        print(clickPoint)

        flag2=False

if flag2:
    for i in range(3):
        temp_obj = my_objects[i]
        for each in temp_obj:
            if inside(clickPoint, each._button):
                each._button.setFill('green')
                flag1=False
                break
        else:
            continue
        break

if flag1 and flag2:
    print(clickPoint)

使用标志变量。现在在上面的代码中,首先设置了flag1=Trueflag2=True。执行第一个if块,如果clickPoint不是,则打印。flag2设置为False

我为什么要这么做?以确保它在下一个if语句中失败。因此,没有执行任何for循环。

下一部分,如果以东十一〔13〕不是以东十一〔18〕的话。下一个if被执行,注意这里是我做了一个小改动的地方!

1
my_objects = {0: meal_objects, 1: build_meal_objects, 2: ingredient_objects}

创建一个dict对象,并使用range(your_dict_size)逐个获取它们。请注意,这样可以添加越来越多的对象。

还记得我解释过退出嵌套循环吗?事情就是这样。一旦你得到你想要的,那就是控制出口,不再运行for循环。

并将flag1设置为False

注:两个标志变量的原因是为了确保在前两个if失败时始终执行您的else部分(这里是最后一个if)。


可能最简单(也是最灵活的方法)是将它们放入函数中并强制执行return,例如:

1
2
3
4
5
6
7
8
9
10
def f(clickPoint, *lists):
    if clickPoint is None:
        # or raise an exception instead as seems more an exception than natural
        return (None, None)
    for lst in lists:
        for item in lst:
            if inside(clickPoint, item._button):
                item._button.setFill('green')
                return (lst, item)
    return (None, None)

然后称之为:

1
lst, item = f(clickPoint, meal_objects, build_meal_objects, ingredient_objects)

这意味着只有所有列表的第一个元素将有一个填充集,它返回对列表和已设置项的引用,如果您想进行打印,可以稍后检查。

如:

1
2
3
4
if (lst, item) == (None, None):
    # handle that nothing was set?
else:
    # you know which button (`item`) in which list (`lst`) had its fill changed

我想如果您真的有必要,您可以使用Python的for/else语法,但这需要您将序列有效地链接到单个for,以便发布break,例如:

1
2
3
4
5
6
for lst, item in ((lst, item) for lst in (meal_objects, build_meal_objects, ingredient_objects) for item in lst):
    if inside(clickPoint, item._button):
        item._button.setFill('green')
        break
else: # this only enters if `break` was NOT issued in the for-loop
    print('nothing set')


似乎您可以很好地使用itertools.chain将for循环压缩为一个块。这样,您就可以循环遍历列表中的所有项,并在到达第一个"内部"项时立即停止。

1
2
3
4
5
6
7
8
9
10
from itertools import chain

if clickPoint is None:
    print(clickPoint)
for each in chain(meal_objects, build_meal_objects, ingredient_objects):
    if inside(clickPoint, each._button):
        each._button.setFill('green')
        break
else:
    print(clickPoint)

我想这就是你想做的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
if clickPoint is None:
    print(clickPoint)
else:
    called = False
    for each in meal_objects:
        if inside(clickPoint, each._button):
            each._button.setFill('green')
            called = True
            break
    if not called:
        for each in build_meal_objects:
            if inside(clickPoint, each._button):
                each._button.setFill('green')
                called = True
                break
        if not called:
            for each in ingredient_objects:
                if inside(clickPoint, each._button):
                    each._button.setFill('green')
                    called = True
                    break
            if not called:
                print(clickPoint)

这确保只有一个for循环调用each._button.setFill('green')。如果没有人调用它,那么就运行print语句。