Không có Lambda Multiline trong Python: Tại sao không?


334

Tôi đã nghe nói rằng lambdas multiline không thể được thêm vào Python bởi vì chúng sẽ xung đột về mặt cú pháp với các cấu trúc cú pháp khác trong Python. Tôi đã suy nghĩ về điều này trên xe buýt ngày hôm nay và nhận ra rằng tôi không thể nghĩ ra một cấu trúc Python duy nhất mà lambdas đa dòng đụng độ. Cho rằng tôi biết ngôn ngữ khá tốt, điều này làm tôi ngạc nhiên.

Bây giờ, tôi chắc chắn rằng Guido có một lý do để không bao gồm lambdas đa dòng trong ngôn ngữ, nhưng vì tò mò: một tình huống trong đó bao gồm một lambda đa dòng sẽ mơ hồ? Là những gì tôi đã nghe là sự thật, hoặc có một số lý do khác mà Python không cho phép lambdas nhiều dòng?


12
phiên bản tl; dr: vì Python là ngôn ngữ lười biếng không có khối {} và vì vậy điều này không được phép để giữ một thiết kế cú pháp nhất quán.
Andrew

11
Ngoài ra: Tôi hoàn toàn ngạc nhiên khi không ai đề cập đến điều này trong các câu trả lời ... Bạn có thể kết thúc dòng với ký tự \ trong Python và tiếp tục đến dòng tiếp theo ... Thông tin này thay thế toàn bộ câu hỏi này vì vậy ...
Andrew


"Thiết kế cú pháp"
nicolas 18/11/19

Điều này sẽ yêu cầu cho phép các câu lệnh bên trong các biểu thức. Nếu bạn sẽ làm điều đó, bạn không cần lambdabiểu thức ở nơi đầu tiên; bạn chỉ có thể sử dụng các defcâu trong biểu thức.
chepner

Câu trả lời:


153

Nhìn vào những điều sau đây:

map(multilambda x:
      y=x+1
      return y
   , [1,2,3])

Đây có phải là một lambda trở lại (y, [1,2,3])(do đó bản đồ chỉ nhận được một tham số, dẫn đến lỗi)? Hay nó trở lại y? Hoặc là lỗi cú pháp, vì dấu phẩy trên dòng mới bị đặt sai vị trí? Làm thế nào Python biết những gì bạn muốn?

Trong parens, thụt đầu dòng không thành vấn đề với trăn, vì vậy bạn không thể làm việc rõ ràng với multilines.

Đây chỉ là một đơn giản, có lẽ có nhiều ví dụ hơn.


106
họ có thể buộc sử dụng dấu ngoặc đơn nếu bạn muốn trả lại một tuple từ lambda. IMO, điều này đáng lẽ phải luôn được thi hành để ngăn chặn sự mơ hồ như vậy, nhưng ồ.
mpen

26
Đây là một sự mơ hồ đơn giản phải được giải quyết bằng cách thêm một tập hợp các parens, một cái gì đó đã tồn tại ở nhiều nơi, ví dụ như các biểu thức trình tạo được bao quanh bởi các đối số khác, gọi một phương thức trên một số nguyên (mặc dù điều này không phải là trường hợp vì tên hàm không thể bắt đầu bằng một chữ số) và dĩ nhiên cả lambdas một dòng (có thể là các biểu thức dài được viết trên nhiều dòng). Lambdas nhiều dòng sẽ không đặc biệt khác với những trường hợp mà nó đảm bảo loại trừ chúng trên cơ sở đó. Đây là câu trả lời thực sự.
nmclean

3
Tôi thích làm thế nào có những ngôn ngữ đáng kinh ngạc giải quyết nó, nhưng bằng cách nào đó có một số lý do sâu sắc tại sao nó được cho là rất khó nếu không thể
nicolas 18/11/19

1
@nicolas đó là con trăn một cách ngắn gọn
javadba

Lý do tại sao tôi không sử dụng lambda, do Python kém phát triển.
NoName

634

Guido van Rossum (người phát minh ra Python) tự trả lời câu hỏi chính xác này trong một bài đăng trên blog cũ .
Về cơ bản, ông thừa nhận rằng về mặt lý thuyết là có thể, nhưng mọi giải pháp được đề xuất sẽ không phải là Pythonic:

"Nhưng sự phức tạp của bất kỳ giải pháp được đề xuất nào cho câu đố này là rất lớn, đối với tôi: nó đòi hỏi trình phân tích cú pháp (hay chính xác hơn là từ vựng) để có thể chuyển đổi qua lại giữa các chế độ nhạy cảm thụt và thụt lề, giữ một ngăn xếp về các chế độ và mức thụt đầu dòng. Về mặt kỹ thuật tất cả có thể được giải quyết (đã có một loạt các mức thụt đầu dòng có thể khái quát hóa). Nhưng không ai trong số đó lấy đi cảm giác ruột thịt của tôi rằng tất cả chỉ là một chi tiết phức tạp của Rube Goldberg . "


108
Tại sao đây không phải là câu trả lời hàng đầu? Đó không phải là về lý do kỹ thuật, đó là một sự lựa chọn thiết kế, như đã được nhà phát minh nêu rõ.
Dan Abramov

13
@DanAbramov vì OP đã không đăng nhập trong nhiều năm.
Hợp đồng của giáo sư Falken vi phạm

7
Đối với những người không hiểu tài liệu tham khảo Rube Goldberg, xem: en.wikipedia.org/wiki/Rube_Goldberg_Machine
fjsj

56
Câu trả lời của Guido chỉ là một lý do khác mà tôi ước Python không phụ thuộc vào thụt lề để xác định các khối.
LS

25
Tôi không chắc chắn tôi gọi "cảm giác ruột" là một lựa chọn thiết kế. ;)
Elliot Cameron

54

Điều này nói chung là rất xấu (nhưng đôi khi các lựa chọn thay thế thậm chí còn xấu hơn), vì vậy một cách giải quyết là tạo ra một biểu hiện niềng răng:

lambda: (
    doFoo('abc'),
    doBar(123),
    doBaz())

Mặc dù vậy, nó sẽ không chấp nhận bất kỳ nhiệm vụ nào, vì vậy bạn sẽ phải chuẩn bị dữ liệu trước. Nơi tôi thấy hữu ích này là trình bao bọc PySide, nơi đôi khi bạn có các cuộc gọi lại ngắn. Viết các chức năng thành viên bổ sung sẽ còn xấu xí hơn. Thông thường bạn sẽ không cần điều này.

Thí dụ:

pushButtonShowDialog.clicked.connect(
    lambda: (
    field1.clear(),
    spinBox1.setValue(0),
    diag.show())

2
Ông chủ của tôi chỉ yêu cầu một cái gì đó như thế này trong ứng dụng PyQt của chúng tôi. Tuyệt vời!
TheGerm

1
Cảm ơn vì điều này, tôi cũng đang tìm kiếm một cách tốt để sử dụng lambdas ngắn (nhưng vẫn đa dòng) làm cuộc gọi lại cho UI PySide của chúng tôi.
Michael Leonard

Và bây giờ tôi đã thấy điều này ngay lập tức đề xuất sử dụng lambda argsetattr(arg, 'attr','value')lật đổ "không có bài tập ...". Và sau đó là đánh giá ngắn mạch andor... đó là Javascript thực hiện nó. Chìm rễ vào bạn, giống như cây thường xuân vào tường. Tôi gần như hy vọng tôi quên điều này qua Xmas.
nigel 222

khá thông minh - và khá dễ đọc. Bây giờ - về những bài tập (thiếu ..) ..
javadba

@ nigel222 tại sao cảm thấy xấu hổ? Ngôn ngữ python đang làm tê liệt cơ bản - nhưng đó là một sử dụng anyways cho nhiều khoa học dữ liệu . Vì vậy, chúng tôi thực hiện điều chỉnh. Tìm cách để thực hiện các tác dụng phụ (thường đủ đơn giản là in / ghi nhật ký!) Và các bài tập (thường đủ để chỉ các bình trung gian!) Nên được xử lý tốt bằng ngôn ngữ. Nhưng họ thậm chí không được hỗ trợ (ít nhất là nếu bạn làm theo PEPhướng dẫn)
javadba

17

Một vài liên kết có liên quan:

Trong một thời gian, tôi đã theo dõi sự phát triển của Reia, ban đầu sẽ có cú pháp dựa trên thụt lề của Python với các khối Ruby, tất cả nằm trên Erlang. Tuy nhiên, nhà thiết kế đã từ bỏ sự nhạy cảm của vết lõm, và bài đăng này ông viết về quyết định đó bao gồm một cuộc thảo luận về các vấn đề mà ông gặp phải với sự thụt dòng + khối đa dòng, và sự đánh giá cao mà ông đạt được đối với các vấn đề / quyết định thiết kế của Guido:

http: //www.unlrictnov ERIC.com/2009/03/indentation-sensergy-post-mortem.html

Ngoài ra, đây là một đề xuất thú vị cho các khối theo phong cách Ruby trong Python Tôi đã chạy qua nơi Guido gửi phản hồi không thực sự bắn hạ nó (mặc dù không biết liệu có bất kỳ vụ bắn hạ nào sau đó không):

http://tav.espians.com/ruby-style-blocks-in-python.html


12

[Chỉnh sửa] Đọc câu trả lời này. Nó giải thích tại sao lambda multiline không phải là một điều.

Nói một cách đơn giản, đó là unpythonic. Từ bài đăng trên blog của Guido van Rossum:

Tôi tìm thấy bất kỳ giải pháp không thể chấp nhận được mà nhúng một khối dựa trên thụt vào giữa một biểu thức. Vì tôi tìm thấy cú pháp thay thế cho nhóm câu lệnh (ví dụ: dấu ngoặc nhọn hoặc từ khóa bắt đầu / kết thúc) không thể chấp nhận được, điều này khá nhiều làm cho lambda nhiều dòng trở thành một câu đố không thể giải được.


10

Hãy để tôi trình bày cho bạn một bản hack vinh quang nhưng đáng sợ:

import types

def _obj():
  return lambda: None

def LET(bindings, body, env=None):
  '''Introduce local bindings.
  ex: LET(('a', 1,
           'b', 2),
          lambda o: [o.a, o.b])
  gives: [1, 2]

  Bindings down the chain can depend on
  the ones above them through a lambda.
  ex: LET(('a', 1,
           'b', lambda o: o.a + 1),
          lambda o: o.b)
  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)

Bây giờ bạn có thể sử dụng LETmẫu này như sau:

map(lambda x: LET(('y', x + 1,
                   'z', x - 1),
                  lambda o: o.y * o.z),
    [1, 2, 3])

cung cấp cho: [0, 3, 8]



Điều này thật tuyệt! Tôi nghĩ rằng tôi sẽ sử dụng điều này vào lần tới khi tôi viết Python. Tôi chủ yếu là một lập trình Lisp và JS, và thiếu dòng Lambada đa đau. Đây là một cách để có được điều đó.
Christopher Dumas

7

Tôi có tội hành nghề bẩn hack này trong một số dự án của tôi mà là đơn giản hơn chút:

    lambda args...:( expr1, expr2, expr3, ...,
            exprN, returnExpr)[-1]

Tôi hy vọng bạn có thể tìm ra cách để giữ pythonic nhưng nếu bạn phải làm điều này ít đau đớn hơn so với sử dụng exec và thao túng toàn cầu.


6

Hãy để tôi thử giải quyết vấn đề phân tích cú pháp @balpha. Tôi sẽ sử dụng dấu ngoặc đơn xung quanh lamda multiline. Nếu không có dấu ngoặc đơn, định nghĩa lambda là tham lam. Vì vậy, lambda trong

map(lambda x:
      y = x+1
      z = x-1
      y*z,
    [1,2,3]))

trả về một hàm trả về (y*z, [1,2,3])

Nhưng

map((lambda x:
      y = x+1
      z = x-1
      y*z)
    ,[1,2,3]))

có nghĩa

map(func, [1,2,3])

trong đó func là lambda đa dòng trả về y * z. Nó có hoạt động không?


1
Tôi nghĩ rằng cái trên cùng sẽ trả về map(func, [1,2,3])và cái dưới cùng sẽ là một lỗi vì hàm map không có đủ đối số. Ngoài ra có một số dấu ngoặc đơn thêm trong mã.
Samy Bencherif

thả nó vào pycharm chạy python2.7.13, nó sẽ báo lỗi cú pháp.
simbo1905

dấu ngoặc đơn phụ
Samy Bencherif

4

(Đối với bất cứ ai vẫn quan tâm đến chủ đề.)

Xem xét điều này (bao gồm cả việc sử dụng các giá trị trả về của câu lệnh trong các câu lệnh tiếp theo trong lambda "multiline", mặc dù nó xấu đến mức nôn mửa ;-)

>>> def foo(arg):
...     result = arg * 2;
...     print "foo(" + str(arg) + ") called: " + str(result);
...     return result;
...
>>> f = lambda a, b, state=[]: [
...     state.append(foo(a)),
...     state.append(foo(b)),
...     state.append(foo(state[0] + state[1])),
...     state[-1]
... ][-1];
>>> f(1, 2);
foo(1) called: 2
foo(2) called: 4
foo(6) called: 12
12

Điều này không hoạt động khi được gọi lần thứ hai với các tham số khác nhau và gây rò rỉ bộ nhớ trừ khi dòng đầu tiên là state.clear()do các đối số mặc định chỉ được tạo một lần khi hàm được tạo.
Matthew D. Scholefield

1

Bạn chỉ có thể sử dụng dấu gạch chéo ( \) nếu bạn có nhiều dòng cho hàm lambda của mình

Thí dụ:

mx = lambda x, y: x if x > y \
     else y
print(mx(30, 20))

Output: 30

Câu hỏi liên quan đến việc sử dụng nhiều hơn 1 biểu thức hơn là hơn 1 dòng chữ.
Tomas Zubiri

1

Tôi đang bắt đầu với python nhưng đến từ Javascript, cách rõ ràng nhất là trích xuất biểu thức dưới dạng hàm ....

Ví dụ có sẵn, biểu thức nhân (x*2)được trích xuất dưới dạng hàm và do đó tôi có thể sử dụng multiline:

def multiply(x):
  print('I am other line')
  return x*2

r = map(lambda x : multiply(x), [1, 2, 3, 4])
print(list(r))

https://repl.it/@datracka/python-lambda-f ghép

Có lẽ nó không trả lời chính xác câu hỏi nếu đó là cách thực hiện multiline trong chính biểu thức lambda , nhưng trong trường hợp ai đó có chủ đề này tìm cách gỡ lỗi biểu thức (như tôi) tôi nghĩ nó sẽ giúp


2
Tại sao tôi sẽ làm điều đó và không chỉ đơn giản là viết map(multiply, [1, 2, 3])?
thothal

0

Về chủ đề hack xấu xí, bạn luôn có thể sử dụng kết hợp execvà một hàm thông thường để xác định hàm multiline như thế này:

f = exec('''
def mlambda(x, y):
    d = y - x
    return d * d
''', globals()) or mlambda

Bạn có thể gói nó thành một chức năng như:

def mlambda(signature, *lines):
    exec_vars = {}
    exec('def mlambda' + signature + ':\n' + '\n'.join('\t' + line for line in lines), exec_vars)
    return exec_vars['mlambda']

f = mlambda('(x, y)',
            'd = y - x',
            'return d * d')

0

Tôi chỉ chơi một chút để cố gắng thực hiện một sự hiểu biết chính tả với giảm bớt, và đưa ra một bản hack lót này:

In [1]: from functools import reduce
In [2]: reduce(lambda d, i: (i[0] < 7 and d.__setitem__(*i[::-1]), d)[-1], [{}, *{1:2, 3:4, 5:6, 7:8}.items()])                                                                                                                                                                 
Out[3]: {2: 1, 4: 3, 6: 5}

Tôi chỉ cố gắng làm giống như những gì đã được thực hiện trong phần hiểu chính tả Javascript này: https://stackoverflow.com/a/11068265


0

Đây là một triển khai thú vị hơn của lambdas nhiều dòng. Không thể đạt được vì cách python sử dụng thụt lề như một cách để cấu trúc mã.

Nhưng may mắn cho chúng tôi, định dạng thụt lề có thể bị vô hiệu hóa bằng cách sử dụng mảng và dấu ngoặc đơn.

Như một số đã chỉ ra, bạn có thể viết mã của mình như sau:

lambda args: (expr1, expr2,... exprN)

Về lý thuyết, nếu bạn được đảm bảo có đánh giá từ trái sang phải thì nó sẽ hoạt động nhưng bạn vẫn mất các giá trị được truyền từ biểu thức này sang biểu thức khác.

Một cách để đạt được điều đó dài dòng hơn một chút là có

lambda args: [lambda1, lambda2, ..., lambdaN]

Trường hợp mỗi lambda nhận được đối số từ cái trước.

def let(*funcs):
    def wrap(args):
        result = args                                                                                                                                                                                                                         
        for func in funcs:
            if not isinstance(result, tuple):
                result = (result,)
            result = func(*result)
        return result
    return wrap

Phương pháp này cho phép bạn viết một cái gì đó hơi lisp / lược đồ như thế nào.

Vì vậy, bạn có thể viết những thứ như thế này:

let(lambda x, y: x+y)((1, 2))

Một phương pháp phức tạp hơn có thể được sử dụng để tính toán cạnh huyền

lst = [(1,2), (2,3)]
result = map(let(
  lambda x, y: (x**2, y**2),
  lambda x, y: (x + y) ** (1/2)
), lst)

Điều này sẽ trả về một danh sách các số vô hướng để nó có thể được sử dụng để giảm nhiều giá trị thành một.

Có nhiều lambda chắc chắn sẽ không hiệu quả lắm nhưng nếu bạn bị hạn chế, đó có thể là một cách tốt để hoàn thành công việc nhanh chóng sau đó viết lại như một chức năng thực tế sau này.


-3

bởi vì hàm lambda được coi là một dòng, vì đây là dạng đơn giản nhất của hàm, an entrance, then return


1
Không, bất cứ ai viết chương trình hướng sự kiện sẽ nói với bạn rằng lambda đa dòng rất quan trọng.
Akangka
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.