列出python中的理解行为

Joh*_*ank 0 python list-comprehension enumerate

我正在与Codeskulptor合作解决岩石碰撞问题.我想检查岩石之间的碰撞,我的岩石列在清单中.我想出了构建组合列表然后检查碰撞的解决方案.我没有itertools可用.我的组合列表是这样创建的:

def combinations(items):
    n_items = [(n,item) for n,item in enumerate(items)]
    return [(item,item2) for n,item in n_items for m,item2 in n_items[n:] if n != m]

letters = ['A','B','C','D']
print combinations(letters)

[('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'C'), ('B', 'D'), ('C', 'D')] 
Run Code Online (Sandbox Code Playgroud)

结果还可以.

在尝试使用函数之前,我尝试在一个衬里中执行此操作:

def combinations2(items):
    return [(item,item2) for n,item in enumerate(items) for m,item2 in enumerate(items[n:]) if n != m]

letters = ['A','B','C','D']
print combinations2(letters)
Run Code Online (Sandbox Code Playgroud)

但结果是完全不同和错误的:

[('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'B'), ('B', 'D'), ('C', 'C'), ('C', 'D'), ('D', 'D')]
Run Code Online (Sandbox Code Playgroud)

列表理解对我来说仍然是一个黑魔法.我无法解释这种行为,想了解错误了.我知道我的双线解决方案要快得多,因为枚举只执行一次而不是使用.但错误的输出对我来说是无法解释的,特别是当BC丢失并且BB CC DD加倍时,AA缺失.

有人能帮我吗?

Mar*_*ers 6

理解列表理解时要做的第一件事是将其扩展为常规的for循环集.从左到右阅读循环并相应地嵌套.

工作代码:

def combinations(items):
    n_items = []
    for n,item in enumerate(items):
        n_items.append((n,item))
    result = []
    for n, item in n_items:
        for m, item2 in n_items[n:]:
            if n != m:
                result.append((item, item2))
    return result
Run Code Online (Sandbox Code Playgroud)

而你的尝试不起作用:

def combinations2(items):
    result = []
    for n, item in enumerate(items):
        for m, item2 in enumerate(items[n:]):
            if n != m:
                result.append((item, item2))
    return result
Run Code Online (Sandbox Code Playgroud)

也许这样就可以更容易地看出两个版本之间出了什么问题.

你的版本只是 切片items,而不是生成的索引enumerate().当您的版本重新编号切片[(0, 'A'), (1, 'B'), (2, 'C'), (3, 'D')][(1, 'B'), (2, 'C'), (3, 'D')],原始版本会切换到等[(0, 'B'), (1, 'C'), (2, 'D')].这反过来会导致错误的输出.

通过向enumerate()函数添加第二个参数来启动较高索引的内部循环,该函数是开始编号的索引:

def combinations2(items):
    result = []
    for n, item in enumerate(items):
        for m, item2 in enumerate(items[n:], n):
            if n != m:
                result.append((item, item2))
    return result
Run Code Online (Sandbox Code Playgroud)

回到单线:

def combinations2(items):
    return [(item, item2) for n, item in enumerate(items) for m, item2 in enumerate(items[n:], n) if n != m]
Run Code Online (Sandbox Code Playgroud)

这可以正常工作:

>>> def combinations2(items):
...     return [(item, item2) for n, item in enumerate(items) for m, item2 in enumerate(items[n:], n) if n != m]
... 
>>> letters = ['A','B','C','D']
>>> combinations2(letters)
[('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'C'), ('B', 'D'), ('C', 'D')]
Run Code Online (Sandbox Code Playgroud)

请注意,您可以进一步简化它; 唯一的时间n == mTrue每个内循环的第一次迭代.只需items将内部列表的列表进一步切片一个值 ; 从外面开始enumerate()1,放下内部enumerate()并放弃n != m测试:

def combinations3(items):
    result = []
    for n, item in enumerate(items, 1):
        for item2 in items[n:]:
            result.append((item, item2))
    return result
Run Code Online (Sandbox Code Playgroud)

或作为列表理解:

def combinations3(items):
    return [(item, item2) for n, item in enumerate(items, 1) for item2 in items[n:]]
Run Code Online (Sandbox Code Playgroud)