Hint bir lambda işlevi yazmak mümkün mü?


93

Şu anda, Python'da, bir işlevin parametreleri ve dönüş türleri aşağıdaki gibi ipuçlarıyla yazılabilir:

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

Bu, işlevin iki dize aldığını ve bir tamsayı döndürdüğünü gösterir.

Ancak, bu sözdizimi aşağıdaki gibi görünen lambdalarla oldukça kafa karıştırıcıdır:

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

Hem parametrelere hem de dönüş türlerine tür ipuçları eklemeyi denedim ve sözdizimi hatasına neden olmayacak bir yol bulamıyorum.

Hint bir lambda işlevi yazmak mümkün mü? Değilse, lambdaları tip ipucu verme planları var mı veya herhangi bir neden var mı (açık sözdizimi çakışmasının yanı sıra) neden olmasın?


Bir lambda için olağan kullanım durumunun başka bir işlevin içine yerleştirildiğini düşünüyorum (bir yerde tanımlanan ve tamamen farklı bir yerde çağrılan normal işlevlerin aksine). Bu işlevdeki türleri zaten biliyorsanız, o zaman (ilke olarak), lambda'nın hangi türleri alacağını bulmak oldukça kolay olacaktır. Ek olarak, ek açıklamaları desteklemek için sözdizimini değiştirmek lambda'nızın okunmasını zorlaştırır .
mgilson

Bunu neden yapmak istersiniz? Lambda sözdizimi, çok kısıtlı bağlamlarda, örneğin yerleşik keyargüman olarak kullanılmak üzere "atma" işlevleri içindir sorted. Bu kadar sınırlı bağlamlarda yazı tipi ipuçları eklemede gerçekten bir nokta görmüyorum. Ek olarak lambda, IMHO noktasını tamamen kaçırmak için yazı ipuçları eklemek için PEP-526 değişken ek açıklamalarını kullanmak . lambdaSözdizimi tanımlamak amaçlanmıştır anonim fonksiyonlar. Kullanmanın lambdave hemen bir değişkene bağlamanın anlamı nedir? Sadece kullan def!
Luciano Ramalho

Yanıtlar:


102

Python 3.6 ve üzeri sürümlerde PEP 526 değişken ek açıklamalarını kullanarak bunu yapabilirsiniz . Sonucu atadığınız değişkene jeneriklambda ile açıklama ekleyebilirsiniz :typing.Callable

from typing import Callable

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

Bu, tür ipucu bilgisini işlev nesnesinin kendisine değil, yalnızca nesneyi içinde sakladığınız ad alanına iliştirir, ancak genellikle tür ipucu amacıyla ihtiyacınız olan tek şey budur.

Bununla birlikte, bunun yerine bir işlev deyimi de kullanabilirsiniz; Bir lambdateklifin tek avantajı, daha büyük bir ifadenin içine basit bir ifade için bir işlev tanımı koyabilmenizdir . Ancak yukarıdaki lambda daha büyük bir ifadenin parçası değildir, yalnızca bir atama ifadesinin bir parçasıdır ve onu bir isme bağlar. Bir def func(var1: str, var2: str): return var1.index(var2)ifadenin tam olarak elde edeceği şey budur .

Eyaletler için dokümantasyon olarak ayrıca açıklama *argsveya **kwargsargüman ekleyemeyeceğinizi unutmayın Callable:

İsteğe bağlı veya anahtar kelime bağımsız değişkenlerini gösteren bir sözdizimi yoktur; bu tür işlev türleri nadiren geri arama türleri olarak kullanılır.

Bu sınırlama, bir yöntem içeren bir PEP 544 protokolü__call__ için geçerli değildir ; hangi argümanların kabul edilmesi gerektiğine dair anlamlı bir tanıma ihtiyacınız varsa bunu kullanın. Python 3.8'e ihtiyacınız var veya bir backport için typing-extensionsprojeyi kurmanız gerekiyor :

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

İçin lambdaİfade kendisi , herhangi bir ek açıklamaları (Python'un tip ipucu inşa edildiği sözdizimi) kullanamazsınız. Sözdizimi yalnızca defişlev ifadeleri için kullanılabilir .

Gönderen - PEP 3107 Fonksiyon Ek Açıklamalar :

lambda'nın sözdizimi ek açıklamaları desteklemez. Lambda'nın sözdizimi, parametre listesinin etrafına parantezler eklenerek açıklamaları desteklemek için değiştirilebilir. Ancak bu değişikliğin yapılmamasına karar verildi çünkü:

  • Uyumsuz bir değişiklik olur.
  • Lambda'lar yine de kısırlaştırıldı.
  • Lambda her zaman bir işleve dönüştürülebilir.

Ek açıklamaları yine de doğrudan nesneye ekleyebilirsiniz, function.__annotations__nitelik yazılabilir bir sözlüktür:

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

Elbette yazım ipuçlarınız üzerinde bir statik analizci çalıştırmak istediğinizde, bunlar gibi dinamik ek açıklamalar size yardımcı olmayacak.


1
Cevap o func: Callable[[str, str], int] = lambda var1, var2: var1.index(var2)zaman neden daha iyi bir cevap değil def func(var1: str, var2: str) -> int: return var1.index(var2)???
Guido van Rossum

@GuidovanRossum: çünkü bu farklı bir sorunun cevabı . :-) Buradaki soru, bir işlevi tanımlamakla aynı sonucu veren bir lambda'ya tür ipucunun nasıl uygulanacağıydı. Yine de, neden bir lambda kullanmamak isteyebileceğinizi anlatan bir bölüm ekleyeceğim.
Martijn Pieters

2
Hayır, kelimenin tam anlamıyla alındığında cevap "Hayır, mümkün değil, bunu değiştirmeye yönelik bir plan yok ve nedenler öncelikle sözdizimseldir - ve adlandırılmış bir işlevi tanımlamak ve ona açıklama eklemek yeterince kolaydır." Ayrıca, önerilen geçici çözümün (belirli bir Çağrılabilir tipte bir değişken yaratma), lambda büyük bir çağrı veya veri yapısının içine gömülmüşse çok yararlı olmadığını, dolayısıyla herhangi bir şekilde bir işlevi tanımlamaktan "daha iyi" olmadığını not ediyorum. (Daha kötü olduğunu düşünüyorum çünkü Çağrılabilir notasyonu çok okunabilir değil.)
Guido van Rossum

Çözüm ayrıca , ömrü boyunca farklı lambdalar atanabilen belirli bir Çağrılabilir tipte bir değişken tanımlıyormuş gibi görünerek dikkati dağıtır . Bu, bir işlevi tanımlamaktan farklıdır (en azından bir def'i yeniden tanımlarsanız veya yeniden atarsanız, ancak Çağrılabilir tipte bir değişken değil) ve orijinal sorudan daha da uzakta "Tip ek açıklamaları olan bir lambda istiyorum". Bu durumda lambda formunu def formu yerine tutmanın bir faydası olmadığını iddia etmeye devam ediyorum. (Ve tip ek açıklaması olmasa bile şunu söyleyebilirim.)
Guido van Rossum

1
@GuidovanRossum: Sizi kendi cevabınızı yazmaya davet edebilir miyim? Dilin babası olarak özel statünüzle, vereceğiniz bir cevap, verilen bir zamanda bu cevabı kolaylıkla geride bırakabilir ve tam olarak içinde olması gerektiğini düşündüğünüz şeyi yazabilirsiniz.
Martijn Pieters

47

Python 3.6'dan beri şunları yapabilirsiniz (bkz. PEP 526 ):

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

Bu cevabı anlamıyorum: türünü nerede belirliyorsunuz x?
stenci

3
@stenci is_evenFonksiyon Callablebir intargüman bekleyen bir fonksiyondur , dolayısıyla x bir an int.
Ocak

Şimdi anlıyorum. İlk bakışta, argümanlarının türünü değil, sadece lambda tarafından döndürülen türü açıkladığınızı düşündüm.
stenci

4
bu aslında lambda'nın kendisine açıklama eklemiyor: bu açıklamaları, açıklamalı bir işlev için yapabildiğiniz gibi lambda nesnesinden alamazsınız
cz

3
Python 3.7 ile mypy 0.701 doğru şekilde yazıp bunu kontrol eder: is_even('x')bir tür hatasına neden olur.
Konrad Rudolph

-2

Hayır, mümkün değil, bunu değiştirmeye yönelik bir plan yok ve nedenler öncelikle sözdizimsel.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.