短路清单理解 [英] Short-circuiting list comprehensions

查看:29
本文介绍了短路清单理解的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有好几次我都希望使用 python 语法来短路列表推导式或生成器表达式.

这是一个简单的列表推导,相当于python中的for循环:

  my_list = [1、2、3,土豆",4、5][如果x!='potato',则x用于my_list中的x]结果= []对于my_list中的元素:如果元素!='potato':result.append(element) 

该语言不支持短路的理解.建议的语法,以及等效于python中的循环:

  [x代表my_list中的x,而x!='potato']#->[1、2、3]结果= []对于 my_list 中的元素:如果元素!='potato':result.append(element)别的:休息 

它应该与任意迭代器(包括无限序列)一起使用,并且可以扩展到生成器表达式语法.我知道 list(itertools.takewhile(lambda x:x!='potato'),my_list)是一个选项,但是:

  • 它不是特别的pythonic-不如一会儿理解
  • 它可能无法像CPython理解那样高效或快速
  • 需要额外的步骤来转换输出,而可以直接将其转化为理解,例如 [在我的列表中x的x.lower()]
  • 甚至原始作者似乎也没有很喜欢.

我的问题是,将语法扩展到此用例不是一个好主意吗?或者由于python开发人员认为它几乎没有用,这是不可能的吗?这似乎是对语言的简单补充和有用的功能,但我可能忽略了一些隐藏的细微之处或复杂之处.

相关:

解决方案

如@Paul McGuire所述,结果是在 while 关键字与显式循环中的 while 不对应-那里只是一个 break .

  • 这使Python变得更难学习,因为它增加了另外一件事需要学习."(在此处引用)
  • 它在生成器表达式和列表理解之间增加了另一个区别.似乎也将这种语法添加到列表理解中绝对是不可能的.
  • 我认为与通常的列表理解的一个基本区别是 while 本质上是命令性的,而不是声明性的.它取决于并指示执行顺序,而该顺序不受语言(AFAIK)的保证.我想这就是Haskell的理解中不包含它的原因,而python就是从中窃取了这个想法.

    当然,生成器表达式确实具有方向,但是它们的元素可以预先计算-再次是AFAIK.提到的PEP确实仅针对生成器表达式提出了建议-这是有道理的.

    当然,Python 绝对是一种命令性语言,但这会引发问题.

    从无序集合中选择怎么样?

      [x用于{1,2,3}中的x,而x!= 2] 

    您也没有在简单的 for 循环中使用它,但这是您无法通过语言强制执行的操作. takewhile 回答了这个问题,但这是一个任意答案.


    最后一点,请注意,为了保持一致性,您需要支持 dropwhile .像

    [x for x in my_list from x != 'potato'] 

    与等价的 for 构造的关系更小,这一次,如果 my_list 仅是可迭代的,则不可能短路.

    On several occasions I've wanted python syntax for short-circuiting list comprehensions or generator expressions.

    Here is a simple list comprehension, and the equivalent for loop in python:

    my_list = [1, 2, 3, 'potato', 4, 5]
    [x for x in my_list if x != 'potato']
    
    result = []
    for element in my_list:
        if element != 'potato':
            result.append(element)
    

    There isn't support in the language for a comprehension which short-circuits. Proposed syntax, and equivalent for loop in python:

    [x for x in my_list while x != 'potato']
    # --> [1, 2, 3]
    
    result = []
    for element in my_list:
        if element != 'potato':
            result.append(element)
        else:
            break
    

    It should work with arbitrary iterables, including infinite sequences, and be extendible to generator expression syntax. I am aware of list(itertools.takewhile(lambda x: x != 'potato'), my_list) as an option, but:

    • it's not particularly pythonic - not as readable as a while comprehension
    • it probably can't be as efficient or fast as a CPython comprehension
    • it requires an additional step to transform the output, whereas that can be put into a comprehension directly, e.g. [x.lower() for x in mylist]
    • even the original author doesn't seem to like it much.

    My question is, was there any theoretical wrinkle about why it's not a good idea to extend the grammar to this use case, or is it just not possible because python dev think it would be rarely useful? It seems like a simple addition to the language, and a useful feature, but I'm probably overlooking some hidden subtleties or complications.

    Related: this and this

    解决方案

    Turns out, as @Paul McGuire noted, that it was proposed in PEP 3142 and rejected by Guido:

    I didn't know there was a PEP for that. I hereby reject it. No point wasting more time on it.

    He doesn't give explanations, though. In the mailing list, some of the points against it are:

    • "[comprehension are] a carefully designed 1 to 1 transformation between multiple nested statements and a single expression. But this proposal ignores and breaks that. (here)." That is, the while keyword does not correspond to a while in the explicit loop - it is only a break there.
    • "It makes Python harder to learn because it adds one more thing to learn." (citated here)
    • It adds another difference between generator expressions and list-comprehension. Seems like adding this syntax to list comprehension too is absolutely out of the question.

    I think that one basic difference from the usual list comprehension is that while is inherently imperative, not declarative. It depends and dictates an order of execution, which is not guaranteed by the language (AFAIK). I guess this is the reason it is not included in Haskell's comprehensions, from which python stole the idea.

    Of course, generator expressions does have direction, but their elements may be precomputed - again, AFAIK. The PEP mentioned did propose it only for generator expressions - which makes some sense.

    Of course, Python is an imperative language anyway, but it will raise problems.

    What about choosing out of a non-ordered collection?

    [x for x in {1,2,3} while x!=2]
    

    You don't have it in simple for loops too, but that's something you can't enforce by the language. takewhile answers this question, but it is an arbitrary answer.


    One last point, note that for consistency you will want support for dropwhile. something like

    [x for x in my_list from x != 'potato']
    

    Which is even less related to the equivalent for construct, and this time it is not possibly short circuit if my_list is just an iterable.

    这篇关于短路清单理解的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆