Có thể nhập gợi ý một hàm lambda không?


83

Hiện tại, trong Python, các tham số và kiểu trả về của một hàm có thể được gợi ý như sau:

def func(var1: str, var2: str) -> int:
    return var1.index(var2)

Điều này chỉ ra rằng hàm nhận hai chuỗi và trả về một số nguyên.

Tuy nhiên, cú pháp này rất dễ gây nhầm lẫn với lambdas, trông giống như:

func = lambda var1, var2: var1.index(var2)

Tôi đã thử đưa các gợi ý kiểu vào cả tham số và kiểu trả về, và tôi không thể tìm ra cách nào không gây ra lỗi cú pháp.

Có thể nhập gợi ý một hàm lambda không? Nếu không, có kế hoạch cho lambdas gợi ý kiểu hay bất kỳ lý do nào (ngoài xung đột cú pháp rõ ràng) tại sao không?


Tôi nghĩ rằng trường hợp sử dụng thông thường cho lambda được lồng trong một hàm khác (tương phản với các hàm thông thường được định nghĩa ở một nơi và được gọi ở một nơi hoàn toàn khác). Nếu bạn đã biết các kiểu trong hàm đó, thì (về nguyên tắc), sẽ khá dễ dàng để tìm ra những kiểu mà lambda sẽ nhận. Ngoài ra, việc sửa đổi cú pháp để hỗ trợ các chú thích sẽ chỉ làm cho lambda của bạn khó đọc hơn.
mgilson

Tại sao bạn muốn làm điều đó? Cú pháp lambda dành cho các hàm "vứt bỏ" để sử dụng trong các ngữ cảnh rất hạn chế, ví dụ, làm keyđối số cho tích sortedhợp sẵn. Tôi thực sự không thấy điểm nào trong việc thêm các gợi ý kiểu trong những bối cảnh hạn chế như vậy. Ngoài ra, sử dụng chú thích biến PEP-526 để thêm gợi ý loại để lambdahoàn toàn bỏ sót điểm, IMHO. Các lambdacú pháp được thiết kế để xác định mang tính chất chức năng. Lợi ích của việc sử dụng lambdavà liên kết ngay nó với một biến là gì? Chỉ cần sử dụng def!
Luciano Ramalho

Câu trả lời:


90

Bạn có thể, đại loại, trong Python 3.6 trở lên bằng cách sử dụng các chú thích biến PEP 526 . Bạn có thể chú thích biến mà bạn gán lambdakết quả với biến typing.Callablechung :

from typing import Callable

func: Callable[[str, str], int] = lambda var1, var2: var1.index(var2)

Điều này không đính kèm thông tin gợi ý kiểu vào chính đối tượng hàm, chỉ với không gian tên bạn đã lưu trữ đối tượng, nhưng đây thường là tất cả những gì bạn cần cho mục đích gợi ý kiểu.

Tuy nhiên, bạn cũng có thể chỉ sử dụng một câu lệnh hàm để thay thế; lợi ích duy nhất mà một lambdacung cấp là bạn có thể đặt một định nghĩa hàm cho một biểu thức đơn giản bên trong một biểu thức lớn hơn. Nhưng lambda ở trên không phải là một phần của một biểu thức lớn hơn, nó chỉ là một phần của một câu lệnh gán, ràng buộc nó với một tên. Đó chính xác là những gì một def func(var1: str, var2: str): return var1.index(var2)tuyên bố sẽ đạt được.

Lưu ý rằng bạn cũng không thể chú thích *argshoặc **kwargsđối số riêng biệt, như tài liệu cho Callablecác trạng thái:

Không có cú pháp để chỉ ra các đối số tùy chọn hoặc từ khóa; các kiểu hàm này hiếm khi được sử dụng làm kiểu gọi lại.

Giới hạn đó không áp dụng cho giao thức PEP 544 có phương pháp__call__ ; sử dụng điều này nếu bạn cần một định nghĩa biểu đạt về những lập luận nào nên được chấp nhận. Bạn cần Python 3.8 hoặc cài đặt typing-extensionsdự án cho một backport:

from typing-extensions import Protocol

class SomeCallableConvention(Protocol):
    def __call__(var1: str, var2: str, spam: str = "ham") -> int:
        ...

func: SomeCallableConvention = lambda var1, var2, spam="ham": var1.index(var2) * spam

Đối với chínhlambda biểu thức , bạn không thể sử dụng bất kỳ chú thích nào (cú pháp mà gợi ý kiểu của Python được xây dựng). Cú pháp chỉ có sẵn cho các câu lệnh hàm.def

Từ PEP 3107 - Chú thích hàm :

cú pháp của lambda không hỗ trợ chú thích. Cú pháp của lambda có thể được thay đổi để hỗ trợ các chú thích, bằng cách yêu cầu dấu ngoặc đơn xung quanh danh sách tham số. Tuy nhiên, nó đã được quyết định không thực hiện thay đổi này vì:

  • Nó sẽ là một thay đổi không tương thích.
  • Lambda's dù sao cũng bị vô hiệu hóa.
  • Lambda luôn có thể được thay đổi thành một hàm.

Bạn vẫn có thể đính kèm các chú thích trực tiếp vào đối tượng, function.__annotations__thuộc tính là một từ điển có thể ghi:

>>> def func(var1: str, var2: str) -> int:
...     return var1.index(var2)
...
>>> func.__annotations__
{'var1': <class 'str'>, 'return': <class 'int'>, 'var2': <class 'str'>}
>>> lfunc = lambda var1, var2: var1.index(var2)
>>> lfunc.__annotations__
{}
>>> lfunc.__annotations__['var1'] = str
>>> lfunc.__annotations__['var2'] = str
>>> lfunc.__annotations__['return'] = int
>>> lfunc.__annotations__
{'var1': <class 'str'>, 'return': <class 'int'>, 'var2': <class 'str'>}

Tất nhiên, không phải các chú thích động như thế này sẽ giúp bạn khi bạn muốn chạy một trình phân tích tĩnh trên các gợi ý loại của mình.


1
Nếu câu trả lời là func: Callable[[str, str], int] = lambda var1, var2: var1.index(var2)thì tại sao không phải là câu trả lời tốt hơndef func(var1: str, var2: str) -> int: return var1.index(var2) ???
Guido van Rossum

@GuidovanRossum: vì đó là câu trả lời cho một câu hỏi khác . :-) Câu hỏi ở đây là làm thế nào để áp dụng gợi ý kiểu cho lambda cho kết quả tương tự như xác định một hàm. Tuy nhiên, tôi sẽ thêm một phần bao gồm lý do tại sao bạn có thể không muốn sử dụng lambda.
Martijn Pieters

1
Không, theo nghĩa đen thì câu trả lời phải là "Không, không thể, không có kế hoạch thay đổi điều này và lý do chủ yếu là theo cú pháp - và thật dễ dàng để xác định một hàm được đặt tên và chú thích điều đó." Tôi cũng lưu ý rằng cách giải quyết được đề xuất (tạo một biến với kiểu Callable cụ thể) không hữu ích lắm nếu lambda bị chôn vùi bên trong một cấu trúc dữ liệu hoặc lệnh gọi lớn, vì vậy, nó không có nghĩa là "tốt hơn" so với việc xác định một hàm. (Tôi cho rằng nó tệ hơn vì ký hiệu Callable không dễ đọc.)
Guido van Rossum

Giải pháp cũng đánh lạc hướng bằng cách định nghĩa một biến với một kiểu Callable nhất định có thể được gán các lambdas khác nhau trong suốt thời gian tồn tại của nó. Điều đó khác với việc xác định một hàm (ít nhất mypy sẽ sai nếu bạn xác định lại hoặc gán lại một định nghĩa nhưng không phải là một biến của kiểu Có thể gọi) và xa hơn câu hỏi ban đầu "Tôi chỉ muốn một lambda với chú thích kiểu." Tôi tiếp tục khẳng định rằng không có lợi ích gì khi giữ biểu mẫu lambda thay vì biểu mẫu def trong trường hợp này. (Và tôi sẽ nói rằng ngay cả khi không có chú thích loại nào.)
Guido van Rossum

1
@GuidovanRossum: Vậy tôi có thể mời bạn viết câu trả lời của riêng bạn không? Với tư cách cụ thể của bạn là cha đẻ của ngôn ngữ, một câu trả lời của bạn sẽ dễ dàng vượt xa câu trả lời này, trong thời gian nhất định, và bạn có thể viết chính xác những gì bạn cảm thấy nên có trong đó.
Martijn Pieters

43

Kể từ Python 3.6, bạn có thể (xem PEP 526 ):

from typing import Callable
is_even: Callable[[int], bool] = lambda x: (x % 2 == 0)

Tôi không hiểu câu trả lời này: bạn đặt loại ở xđâu?
stenci 19/03/18

3
@stenci is_evenHàm là một hàm Callablemong đợi một intđối số, vì vậy x là một int.
Tháng Một

Giờ thì tôi đã hiểu. Thoạt nhìn, tôi nghĩ bạn chỉ chú thích kiểu được trả về bởi lambda, không phải kiểu đối số của nó.
stenci

4
điều này là không thực sự chú giải các lambda bản thân: bạn không thể lấy các chú thích từ đối tượng lambda như bạn có thể cho một chức năng chú thích
cz

3
mypy 0.701 với Python 3.7 đánh máy chính xác điều này: is_even('x')gây ra lỗi kiểu.
Konrad Rudolph

-1

Không, không thể, không có kế hoạch nào để thay đổi điều này, và lý do chủ yếu là do cú pháp.

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.