람다 함수에 힌트를 입력 할 수 있습니까?


85

현재 Python에서 함수의 매개 변수와 반환 유형은 다음과 같이 유형 힌트가 될 수 있습니다.

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

이는 함수가 두 개의 문자열을 취하고 정수를 반환 함을 나타냅니다.

그러나이 구문은 다음과 같은 람다와 매우 혼동됩니다.

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

매개 변수와 반환 유형 모두에 유형 힌트를 입력 해 보았지만 구문 오류를 일으키지 않는 방법을 찾을 수 없습니다.

람다 함수에 힌트를 입력 할 수 있습니까? 그렇지 않은 경우 유형 힌트 람다에 대한 계획이 있습니까? 아니면 이유가 있습니까 (명백한 구문 충돌 외에도) 왜 안됩니까?


람다의 일반적인 사용 사례는 다른 함수 내에 중첩되어 있다고 생각합니다 (한 곳에서 정의되고 완전히 다른 곳에서 호출되는 일반 함수와 대조 됨). 해당 함수의 유형을 이미 알고 있다면 (원칙적으로) 람다가 수신 할 유형을 파악하는 것이 매우 쉽습니다. 또한 주석을 지원하도록 구문을 수정하면 람다 를 읽기가 더 어려워 집니다.
mgilson 2015

왜 그렇게 하시겠습니까? 람다 구문은 예를 들어 내장에 대한 key인수로 매우 제한된 컨텍스트 내에서 사용하기위한 "throw-away"함수를위한 것 sorted입니다. 이러한 제한된 컨텍스트에서 유형 힌트를 추가하는 데는 아무런 의미가 없습니다. 또한 PEP-526 변수 주석을 사용하여 lambdaIMHO라는 요점 을 완전히 놓치는 유형 힌트를 추가합니다 . lambda구문을 정의하기위한 것입니다 익명의 기능을. 사용 lambda하고 즉시 변수에 바인딩하는 이유는 무엇입니까? 그냥 사용하십시오 def!
Luciano Ramalho

답변:


92

일종의 Python 3.6 이상에서 PEP 526 변수 주석을 사용하여 할 수 있습니다 . lambda결과를 할당하는 변수 에 typing.Callablegeneric으로 주석을 달 수 있습니다 .

from typing import Callable

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

이것은 타입 힌팅 정보를 함수 객체 자체에 첨부하는 것이 아니라 객체를 저장 한 네임 스페이스에만 첨부하지만 일반적으로 이것은 타입 힌팅 목적에 필요한 모든 것입니다.

그러나 대신 함수 문을 사용할 수도 있습니다. 에서 lambda제공 하는 유일한 장점 은 더 큰 표현식 안에 간단한 표현식에 대한 함수 정의를 넣을 수 있다는 것 입니다. 그러나 위의 람다는 더 큰 표현식의 일부가 아니며 할당 문의 일부일 뿐이며 이름에 바인딩합니다. 이것이 바로 def func(var1: str, var2: str): return var1.index(var2)성명서가 달성 할 수있는 것입니다.

상태에 대한 문서로 주석 *args이나 **kwargs인수를 별도로 지정할 수 없습니다 Callable.

선택적 또는 키워드 인수를 나타내는 구문은 없습니다. 이러한 함수 유형은 콜백 유형으로 거의 사용되지 않습니다.

그 제한은 적용되지 않습니다 PEP 544 프로토콜 A의 __call__방법 ; 어떤 인수를 받아 들여야하는지에 대한 표현적인 정의가 필요한 경우이를 사용하십시오. Python 3.8이 필요 하거나 백 포트 용 typing-extensions프로젝트 를 설치해야합니다 .

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

를 들어 lambda표현 자체 , 당신은 어떤 주석 (파이썬의 유형 힌트가 구축되는 구문)를 사용할 수 없습니다. 구문은 def함수 문 에만 사용할 수 있습니다 .

에서 - PEP 3107 기능 주석 :

lambda의 구문은 주석을 지원하지 않습니다. 람다 구문은 매개 변수 목록을 괄호로 묶어 주석을 지원하도록 변경할 수 있습니다. 그러나 다음과 같은 이유로 변경하지 않기로 결정했습니다 .

  • 호환되지 않는 변경입니다.
  • 어쨌든 Lambda는 중성화됩니다.
  • 람다는 항상 함수로 변경할 수 있습니다.

여전히 주석을 객체에 직접 첨부 할 수 있으며 function.__annotations__속성은 쓰기 가능한 사전입니다.

>>> 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'>}

Not that dynamic annotations like these are going to help you when you wanted to run a static analyser over your type hints, of course.


1
If the answer is func: Callable[[str, str], int] = lambda var1, var2: var1.index(var2) then why isn't a better answer def func(var1: str, var2: str) -> int: return var1.index(var2)???
Guido van Rossum

@GuidovanRossum: because that's the answer to a different question. :-) The question here was how to apply type hinting to a lambda that gives the same result as defining a function. Still, I'll add a section covering why you might want to not use a lambda.
Martijn Pieters

1
No, taken literally the answer should be "No, it is not possible, there are no plans to change this, and the reasons are primarily syntactical --and it's easy enough to define a named function and annotate that." I also note that the proposed workaround (create a variable with a specific Callable type) is not very helpful if the lambda is buried inside a large call or data structure, so it's not in any sense "better" than defining a function. (I'd argue that it's worse because the Callable notation is not very readable.)
Guido van Rossum

The solution also distracts by looking like it's defining a variable with a certain Callable type that could be assigned different lambdas during its lifetime. That's different than defining a function (at least mypy balks if you redefine or reassign a def but not a variable of Callable type) and further away from the original question "I just want a lambda with type annotations." I continue to claim that there is no benefit in keeping the lambda form instead of the def form in this case. (And I would say that even if there were no type annotations.)
Guido van Rossum

1
@GuidovanRossum: Can I invite you to write your own answer then? With your specific status as father of the language, an answer by you would easily outstrip this one, given time, and you get to write exactly what you feel should be in it.
Martijn Pieters

43

Since Python 3.6, you can (see PEP 526):

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

I don't understand this answer: where do you set the type of x?
stenci

3
@stenci The is_even function is a Callable that expects one int argument, so x is an int.
jan

I understand now. At first glance I thought you were annotating only the type returned by the lambda, not the type of its arguments.
stenci

4
this isn't actually annotating the lambda itself: you can't retrieve these annotations from the lambda object as you can for an annotated function
c z

3
mypy 0.701 with Python 3.7 correctly typechecks this: is_even('x') causes a type error.
Konrad Rudolph

-1

No, it is not possible, there are no plans to change this, and the reasons are primarily syntactical.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.