Есть ли эквивалент Python для Haskell 'let'

Есть ли эквивалент Python выражения «let» Haskell, который позволил бы мне написать что-то вроде:

list2 = [let (name,size)=lookup(productId) in (barcode(productId),metric(size)) for productId in list] 

Если нет, то какая будет наиболее читаемая альтернатива?

Добавлено для пояснения синтаксиса let:

 x = let (name,size)=lookup(productId) in (barcode(productId),metric(size)) 

эквивалентно

 (name,size) = lookup(productId) x = (barcode(productId),metric(size)) 

Тем не менее, вторая версия не очень хорошо работает со списками.

7 Solutions collect form web for “Есть ли эквивалент Python для Haskell 'let'”

Вы можете использовать временное понимание списка

 [(barcode(productId), metric(size)) for name, size in [lookup(productId)]][0] 

или, что эквивалентно, выражение генератора

 next((barcode(productId), metric(size)) for name, size in [lookup(productId)]) 

но оба они довольно ужасны.

Другой (ужасный) метод – это временная лямбда, которую вы вызываете немедленно

 (lambda (name, size): (barcode(productId), metric(size)))(lookup(productId)) 

Я думаю, что рекомендованный «Pythonic» способ состоял бы в том, чтобы определить функцию, например

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

Такого нет. Вы можете эмулировать его таким же образом, чтобы его можно было отбросить до исчисления лямбда ( let x = foo in bar <=> (\x -> bar) (foo) ).

Наиболее читаемая альтернатива зависит от обстоятельств. Для вашего конкретного примера я бы выбрал что-то вроде [barcode(productId), metric(size) for productId, (_, size) in zip(productIds, map(lookup, productIds))] (на самом деле уродливый во второй мысли, это проще, если вам тоже не требуется productId , тогда вы можете использовать map ) или явный цикл цикла (в генераторе):

 def barcodes_and_metrics(productIds): for productId in productIds: _, size = lookup(productId) yield barcode(productId), metric(size) 

Последние версии python допускают множественные выражения в выражении генератора, поэтому теперь вы можете сделать что-то вроде:

 list2 = [ barcode(productID), metric(size) for productID in list for (name,size) in (lookup(productID),) ] 

который аналогичен тому, что предлагает Haskell:

 list2 = [ (barcode productID, metric size) | productID <- list , let (name,size) = lookup productID ] 

и денотационно эквивалентно

 list2 = [ (barcode productID, metric size) | productID <- list , (name,size) <- [lookup productID] ] 

Множественные предложения в ответе b0fh – это стиль, который я лично использовал некоторое время, поскольку я считаю, что он обеспечивает большую ясность и не загромождает пространство имен временными функциями. Однако, если скорость является проблемой, важно помнить, что временное построение одного списка элементов заметно длиннее, чем построение одного кортежа.

Сравнивая скорость различных решений в этом потоке, я обнаружил, что уродливый лямбда-хак является самым медленным, за которым следуют вложенные генераторы, а затем решение b0fh. Однако все они были превзойдены одним кортежем:

 list2 = [ barcode(productID), metric(size) for productID in list for (_, size) in (lookup(productID),) ] 

Это может быть не так актуально для вопроса OP, но есть и другие случаи, когда ясность может быть значительно увеличена и скорость достигается в случаях, когда можно было бы использовать понимание списка, используя однокортежи вместо списков для фиктивных итераторов.

Чтобы получить что-то смутно сопоставимое, вам нужно будет сделать два понимания или карты или определить новую функцию. Один из подходов, который еще не был предложен, состоит в том, чтобы разбить его на две строки. Я считаю, что это несколько читаемо; хотя, вероятно, определение вашей собственной функции – это правильный путь:

 pids_names_sizes = (pid, lookup(pid) for pid in list1) list2 = [(barcode(pid), metric(size)) for pid, (name, size) in pids_names_sizes] 

Только гадать, что делает Хаскелл, вот альтернатива. Он использует то, что известно в Python, как «понимание списка».

 [barcode(productId), metric(size) for (productId, (name, size)) in [ (productId, lookup(productId)) for productId in list_] ] 

Вы могли бы включить использование lambda: как предложили другие.

Хотя вы можете просто написать это как:

 list2 = [(barcode(pid), metric(lookup(pid)[1])) for pid in list] 

Вы можете определить LET самостоятельно, чтобы получить:

 list2 = [LET(('size', lookup(pid)[1]), lambda o: (barcode(pid), metric(o.size))) for pid in list] 

или даже:

 list2 = map(lambda pid: LET(('name_size', lookup(pid), 'size', lambda o: o.name_size[1]), lambda o: (barcode(pid), metric(o.size))), list) 

следующим образом:

 import types def _obj(): return lambda: None def LET(bindings, body, env=None): '''Introduce local bindings. ex: LET(('a', 1, 'b', 2), lambda o: [oa, ob]) gives: [1, 2] Bindings down the chain can depend on the ones above them through a lambda. ex: LET(('a', 1, 'b', lambda o: oa + 1), lambda o: ob) gives: 2 ''' if len(bindings) == 0: return body(env) env = env or _obj() k, v = bindings[:2] if isinstance(v, types.FunctionType): v = v(env) setattr(env, k, v) return LET(bindings[2:], body, env) 
Python - лучший язык программирования в мире.