【问题标题】:Inconsistent comprehension syntax?理解语法不一致?
【发布时间】:2013-11-16 23:33:42
【问题描述】:

我只是偶然发现了 python 语法中的一个缺陷——否则我错过了一些东西。

看这个:

[x for x in range(30) if x % 2 == 0]

但这是一个语法错误:

[x for x in range(30) if x % 2 == 0 else 5]

如果你有一个else 子句,你必须写:

[x if x % 2 == 0 else 5 for x in range (30)]

但这是一个语法错误:

[x if x %2 == 0 for x in range(30)]

我错过了什么?为什么会如此不一致?

【问题讨论】:

    标签: python if-statement python-3.x list-comprehension


    【解决方案1】:

    你在这里混合语法。这里有两个不同的概念:

    • 列表理解语法。这里if充当过滤器;是否在迭代中包含一个值。没有else,因为那已经是“不包含”的情况了。

    • conditional expression。这必须始终返回一个值,即“真”或“假”表达式的结果。

    记住:列表推导从循环中产生一系列值。通过使用if,您可以控制输入迭代中有多少元素用于输出。

    另一方面,条件表达式与任何其他表达式一样工作:一个表达式总是产生一个结果;条件表达式允许您在两种可能的结果之间进行选择。但是因为它必须产生一个结果,如果没有else 部分,你就不能写一个结果。

    请注意,表达式可以并且经常嵌套。条件表达式本身包含三个子表达式:

    expr1 if expr2 else expr3
    # ^                   ^
    # \ used when expr2   |
    #   is true           |
    #                     /
    #   used when expr2 is
    #   false
    

    列表推导还包含子表达式。开头的那个(在for <target> in <iterable> 部分之前)是每次迭代执行以在输出列表中生成值的子表达式。迭代器表达式(在in 之后)是另一个。可选的if 过滤器也接受一个表达式。如果您愿意,可以在任何这些地方使用条件表达式。但是,这并不意味着您可以在没有条件表达式语法的其他部分的情况下向列表推导添加额外的 else

    【讨论】:

    • 哦...所以没有办法将else 作为理解的一部分。
    • @Aerovistae:不,它不是语法的一部分,因为没有意义
    • 好吧,如果你想在所有被省略的地方插入空格(或其他东西)......
    • 那么你没有省略,你正在替换并且条件表达式工作得很好。
    • @Aerovistae 在列表组合语法的末尾使用if 是告诉列表组合是否包含它。 [foo for foo in bar if isinstance(foo,basestring)] 检查isinstance(foo,basestring),如果是False,则立即丢弃该foo。如果您想包含foo,无论它是否为basestring,但如果不是(str(foo)?),则可能对其进行处理,然后不要将其放在最后的过滤器中,而是将其分配为条件表达式。 [foo if isinstance(foo,basestring) else str(foo) for foo in bar] 现在列表 comp 查看 bar 中的所有 foos。
    【解决方案2】:

    两者之间的区别在于,第一个结尾的iflist comprehension syntax 的一部分,而if-else 是条件运算符,而不是列表理解语法的任何部分——因为它是列表理解的那部分允许的表达式。

    syntax for the conditional operator如下:

    x if condition1 else y
    

    这将返回被评估的表达式的值,这就是为什么它似乎对您的情况“有效”的原因,尽管它一直在评估并总是返回 - 这是两者之间的关键区别。

    同时,对于列表推导,它会测试条件是否适用,如果条件不计算为根据Truth Value Testing procedure 是真的,不是None 或其他任何东西。

    比较以下(以PEP202为例):

    a = [i if i % 2 == 0 else None for i in range(20)]
    b =  [i for i in range(20) if i % 2 == 0]
    

    a 会是

    [0, None, 2, None, 4, None, 6, None, 8, None, 10, None, 12, None, 14, None, 16, None, 18, None]
    

    b 会是

    [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
    

    这根本不一样,因为无论结果如何,如果列表理解中没有if部分,它仍然会添加它。 p>

    【讨论】:

    • 那么我怎样才能将 if/else 作为理解的一部分呢?这可能吗?
    • @Aerovistae:不,你不能在条件运算符中使用elif
    【解决方案3】:

    我错过了什么?

    以下是一个三元运算(在python中也称为“条件表达式”)

    x if some_boolean else y
    

    计算如下:如果some_booleanTrue,给我x,否则给我y。

    不要将此与理解语法混淆:

    (expression) for (iteration variable) in (iterable) [if (filter)]
    

    条件表达式可以进入(表达式)部分。它与可选的if (filter) 部分没有任何关系。

    【讨论】:

    • 条件表达式是一个三元运算符,就像or是一个二元运算符一样。但是使用“三元运算符”作为条件表达式的名称是不正确的;三元只是指它需要三个操作数的事实,因此三元运算符是表达式的,而不是特定的表达式。 Python 只有一个这样的运算符这一事实在这里并不重要。
    • @MartijnPieters “三元 if” 可能更正确,但我发现在只有一个三元运算符的语言中进一步限定“三元运算符”是不必要的迂腐。我知道说 the 三元运算符会惹恼一些人,但如果只有一个......嗯。 YMMV。
    • 它是一个运算符,有3个操作数,所以三元运算符是一个很好的术语,但它仍然是一个classtype指定,只是像二元运算符,而不是名称。 “条件表达式”是文档中使用的名称。
    猜你喜欢
    • 1970-01-01
    • 2020-09-01
    • 1970-01-01
    • 2019-11-26
    • 2013-11-11
    • 1970-01-01
    • 2014-09-01
    • 1970-01-01
    • 2023-01-02
    相关资源
    最近更新 更多