如何在列表理解中具有多个条件子句?

时间:2018-07-30 03:32:51

标签: python list if-statement list-comprehension

我正在做一些字符串解析,如果字符是字母,则返回1,如果字符是数字,则返回2,如果字符是其他任何字符,则返回。

通常我会使用for循环并像这样附加到它

string = 'hello123...'
values = []
for char in string:
    if char.isalpha():
        values.append(1)
    elif char.isdigit():
        values.append(2)

返回

[1, 1, 1, 1, 1, 2, 2, 2]

符合预期。根据{{​​3}},使用列表推导会更快。所以我尝试了:

values = [1 if char.isalpha else 2 if char.isdigit for char in string]

但是,这给了我一个语法错误,因为期望“ else”。

File "C:/Users/test3.py", line 12
values = [1 if char.isalpha else 2 if char.isdigit for char in string]
                                                     ^
SyntaxError: invalid syntax

如果字符不是字母数字,我希望它不添加任何内容。我的代码有什么问题?

我将不得不执行此功能数十亿次,因此,如果有更好的方法来执行此功能,则欢迎提高效率。

3 个答案:

答案 0 :(得分:5)

如果您根本不想考虑该特定元素,则应在理解结束时将该条件作为 guard

[1 if char.isalpha() else 2 for char in string if char.isdigit() or char.isalpha()]

最后的if char.isdigit() or char.isalpha()消除了所有不满足这些谓词的元素。

话虽如此,为了可读性,我建议至少将翻译部分(可能还有条件部分)分解为一个单独的函数。这种单线的外观虽然很巧妙,但可读性却很差。

答案 1 :(得分:2)

不幸的是,Python不允许您这样做。但是,您可以这样做:

values = [1 if char.isalpha() else 2 for char in string if char.isalnum()]

(如果且仅当char.isalnum()时为char.isalpha() or char.isdigit()

您也可以

values = [value for value in (1 if char.isalpha() else 2 if char.isalnum() else None for char in string) if value]

或等价物(感谢提醒,AChampion)

values = list(filter(None, (1 if char.isalpha() else 2 if char.isalnum() else None for char in string)))

摆脱“无”,虽然我不确定它有多快/多快(它不是建立新列表,仅使用生成器,所以减速可能是很难察觉的。)

答案 2 :(得分:2)

其他答案很好地说明了如何更改列表理解以正确处理非字母数字的情况。相反,我想解决一个假设,即列表理解总是比常规循环快得多。

通常是 ,但是很多时候您可以修改循环以弥补大部分或全部丢失的地面。特别是,在列表上查找append方法相对较慢,因为它涉及在字典中查找某些内容并创建绑定的方法对象。您可以更改代码以在循环之前进行一次查找,并且您的代码最终可能比其他任何版本都快:

values = []
values_append = values.append   # cache this method lookup
for char in string:
    if char.isalpha():
        values_append(1)        # used cached method here
    elif char.isdigit():
        values_append(2)        # and here

以下是使用一百万个字符串的测试时间:

import random, timeit

big_str = "".join(random.choice(['a', '1', '~']) for _ in range(1000000))

def loop_cyon(string):
    values = []
    for char in string:
        if char.isalpha():
            values.append(1)
        elif char.isdigit():
            values.append(2)
    return values

def comp_silvio_mayolo(string):
    return [1 if char.isalpha() else 2 for char in string if char.isdigit() or char.isalpha()]

def comp_amadan1(string):
    return [1 if char.isalpha() else 2 for char in string if char.isalnum()]

def comp_amadan2(string):
    return list(filter(None, (1 if char.isalpha() else 2 if char.isalnum() else None for char in string)))

def loop_blckknght(string):
    values = []
    values_append = values.append
    for char in string:
        if char.isalpha():
            values_append(1)
        elif char.isdigit():
            values_append(2)
    return values

for func in [loop_cyon, comp_silvio_mayolo, comp_amadan1, comp_amadan2, loop_blckknght]:
    print(func.__name__)
    timeit.timeit(lambda: func(big_str), number=10)

我的系统上的输出(Windows 10 64x,Python 3.6):

loop_cyon 2.5896435911574827
comp_silvio_mayolo 2.6970998627145946
comp_amadan1 2.177768147485949
comp_amadan2 2.676028711925028
loop_blckknght 2.244682003625485

所以看起来最好的列表理解仍然比我的循环代码快一点,但幅度不大。而且,我肯定会说,在这种情况下,显式循环更加清晰,而且清晰性可能比性能差异更重要。