有没有相当于Haskell'let'的Python

Per*_*ids 20 python haskell functional-programming let

是否有一个类似于Haskell'let'表达式的Python,它可以让我写出如下内容:

list2 = [let (name,size)=lookup(productId) in (barcode(productId),metric(size)) 
            for productId in list]
Run Code Online (Sandbox Code Playgroud)

如果没有,最可读的替代方案是什么?

添加以澄清let语法:

x = let (name,size)=lookup(productId) in (barcode(productId),metric(size))
Run Code Online (Sandbox Code Playgroud)

相当于

(name,size) = lookup(productId)
x = (barcode(productId),metric(size))
Run Code Online (Sandbox Code Playgroud)

但是,第二个版本与列表推导不太相配.

huo*_*uon 19

您可以使用临时列表理解

[(barcode(productId), metric(size)) for name, size in [lookup(productId)]][0]
Run Code Online (Sandbox Code Playgroud)

或者,等效地,生成器表达式

next((barcode(productId), metric(size)) for name, size in [lookup(productId)])
Run Code Online (Sandbox Code Playgroud)

但这两个都非常可怕.

另一种(可怕的)方法是通过一个临时的lambda,你可以立即调用它

(lambda (name, size): (barcode(productId), metric(size)))(lookup(productId))
Run Code Online (Sandbox Code Playgroud)

我认为推荐的"Pythonic"方式只是定义一个函数,比如

def barcode_metric(productId):
   name, size = lookup(productId)
   return barcode(productId), metric(size)
list2 = [barcode_metric(productId) for productId in list]
Run Code Online (Sandbox Code Playgroud)


小智 10

哪有这回事.你可以用同样的方式模仿它,let而不是lambda演算(let x = foo in bar<=> (\x -> bar) (foo)).

最可读的替代方案取决于具体情况.对于你的具体例子,我会选择类似的东西[barcode(productId), metric(size) for productId, (_, size) in zip(productIds, map(lookup, productIds))](如果你不需要productId,那么你可以使用它,map或者你可以使用)或显式for循环(在生成器中):

def barcodes_and_metrics(productIds):
    for productId in productIds:
        _, size = lookup(productId)
        yield barcode(productId), metric(size)
Run Code Online (Sandbox Code Playgroud)

  • 我担心在Python代码中使用lambda表达式执行此操作可能会产生不良副作用,例如您的门被一群愤怒的Pythonistas带着火把和干草叉踢开. (13认同)
  • 在罗马做到入乡随俗.在大多数编程语言中,与预期的风格和习语相悖是不明智的,在Python中更是如此(可选的妙语:......但在perl中,它是一种艺术形式). (4认同)

b0f*_*0fh 10

最近的python版本允许在生成器表达式中使用多个for子句,因此您现在可以执行以下操作:

list2 = [ barcode(productID), metric(size)
          for productID in list
          for (name,size) in (lookup(productID),) ]
Run Code Online (Sandbox Code Playgroud)

这与Haskell提供的类似:

list2 = [ (barcode productID, metric size)
        | productID <- list
        , let (name,size) = lookup productID ]
Run Code Online (Sandbox Code Playgroud)

和指称相当于

list2 = [ (barcode productID, metric size) 
        | productID <- list
        , (name,size) <- [lookup productID] ]
Run Code Online (Sandbox Code Playgroud)


dal*_*lum 6

b0fh答案中的多个for子句是我个人已经使用了一段时间的样式,因为我相信它提供了更多的清晰度,并且不会使用临时函数混淆命名空间.但是,如果速度是一个问题,重要的是要记住,临时构建一个元素列表所花费的时间比构建一个元组要长得多.

比较这个线程中各种解决方案的速度,我发现丑陋的lambda hack是最慢的,接着是嵌套的生成器,然后是b0fh的解决方案.然而,这些都被一元组赢家所超越:

list2 = [ barcode(productID), metric(size)
          for productID in list
          for (_, size) in (lookup(productID),) ]
Run Code Online (Sandbox Code Playgroud)

这可能与OP的问题不太相关,但是在其他情况下,通过使用单元组而不是虚拟迭代器的列表,可以大大增强清晰度并在人们可能希望使用列表解析的情况下获得速度.


Oli*_*Oli 6

在 Python 3.8 中,:=添加了使用运算符的赋值表达式:PEP 572

这可以像在 Haskell 中一样使用let,尽管不支持可迭代解包。

list2 = [
    (lookup_result := lookup(productId), # store tuple since iterable unpacking isn't supported
     name := lookup_result[0], # manually unpack tuple
     size := lookup_result[1],
     (barcode(productId), metric(size)))[-1] # put result as the last item in the tuple, then extract on the result using the (...)[-1]
    for productId in list1
]
Run Code Online (Sandbox Code Playgroud)

请注意,这与普通的 Python 赋值一样,例如,如果在函数内部使用,则绑定的变量将在整个函数中可访问,而不仅仅是在表达式中。


小智 5

既然您要求最好的可读性,您可以考虑 lambda 选项,但有一个小改动:初始化参数。以下是我自己使用的各种选项,从我尝试的第一个开始到我现在最常用的一个。

假设我们有一个函数(未显示)作为data_structure参数,并且您需要x反复从中获取。

第一次尝试(根据 huon 2012 年的回答):

(lambda x:
    x * x + 42 * x)
  (data_structure['a']['b'])
Run Code Online (Sandbox Code Playgroud)

对于多个符号,这变得不太可读,所以接下来我尝试:

(lambda x, y:
    x * x + 42 * x + y)
  (x = data_structure['a']['b'],
   y = 16)
Run Code Online (Sandbox Code Playgroud)

这仍然不太可读,因为它重复了符号名称。然后我尝试了:

(lambda x = data_structure['a']['b'],
        y = 16:
  x * x + 42 * x + y)()
Run Code Online (Sandbox Code Playgroud)

这几乎读作“let”表达式。当然,作业的定位和格式由您决定。

这个习语很容易通过开头“(”和结尾“()”来识别。

在函数表达式中(也在 Python 中),许多括号往往会堆积在末尾。奇怪的“(”很容易被发现。