Python'da Çok Satırlı Lambda Yok: Neden olmasın?


335

Çok satırlı lambdaların Python'a eklenemeyeceğini söyledim, çünkü Python'daki diğer sözdizimi yapılarıyla sözdizimsel olarak çatışacaklardı. Bunu bugün otobüste düşünüyordum ve çok satırlı lambdaların çarpıştığı tek bir Python yapısı düşünemediğimi fark ettim. Dili çok iyi bildiğim göz önüne alındığında, bu beni şaşırttı.

Şimdi, Guido'nun dile çok satırlı lambdaları dahil etmemekle, ancak meraktan dolayı bir nedeni vardı: Çok satırlı bir lambda eklemenin belirsiz olacağı bir durum nedir? Doğru duyduğum şey mi, yoksa Python'un çok satırlı lambdalara izin vermemesinin başka bir nedeni var mı?


12
tl; dr sürümü: çünkü Python {} blokları olmayan tembel bir dildir ve bu nedenle tutarlı bir sözdizimsel tasarımı korumak için buna izin verilmemiştir.
Andrew

11
Ayrıca: Kimsenin cevaplarda bundan bahsetmediğine şaşırdım ... Python'daki \ karakteriyle satırları bitirebilir ve bir sonraki satıra devam edebilirsiniz ... Bu bilgi bu sorunun yerine geçebilir ...
Andrew


"sözdizimsel tasarım"
nicolas

Bunun için ifadelerin içinde ifadelere izin verilmesi gerekir. Bunu yapacaksanız lambda, ilk etapta ifadelere ihtiyacınız yoktur ; defifadelerde ifadeleri kullanabilirsiniz .
chepner

Yanıtlar:


153

Aşağıdakilere bakın:

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

Bu dönen bir lambda mı (y, [1,2,3])(böylece harita yalnızca bir parametre alır ve hataya neden olur)? Yoksa geri ymi dönüyor ? Yoksa yeni satırdaki virgül yanlış yerleştirilmiş olduğundan sözdizimi hatası mıdır? Python ne istediğini nasıl bilebilir?

Parens içinde, girinti python için önemli değildir, bu nedenle çok satırlılarla net bir şekilde çalışamazsınız.

Bu sadece basit bir örnek, muhtemelen daha fazla örnek var.


107
lambdadan bir demet vermek isterseniz parantez kullanımını zorlayabilirler. IMO, bu tür belirsizlikleri önlemek için her zaman zorunlu kılınmış olmalı, ama iyi.
mpen

26
Bu, fazladan bir parens kümesi ekleyerek çözülmesi gereken basit bir belirsizliktir, zaten birçok yerde var olan bir şey, örneğin diğer argümanlarla çevrili jeneratör ifadeleri, tamsayı bir değişmezde bir yöntem çağırarak (bu, işlev adı bir rakamla başlayamaz) ve elbette tek satır lambdas da (birden çok satırda uzun ifadeler olabilir). Çok hatlı lambdalar, bu esaslar dışında onları hariç tuttuğunu garanti eden bu davalardan özellikle farklı olmayacaktır. Bu gerçek cevaptır.
nmclean

3
Onunla ilgilenen gazilyon dillerinin nasıl bir endişe duymadığını seviyorum, ancak bir şekilde imkansız değilse bile çok zor olmasının bazı derin nedenleri var
nicolas

1
@nicolas Özetle python
javadba

Python'da az gelişmiş lambda kullanmamamın nedeni.
NoName

635

Guido van Rossum (Python'un mucidi) bu soruyu kendisi eski bir blog yazısında cevaplıyor .
Temel olarak, teorik olarak mümkün olduğunu, ancak önerilen herhangi bir çözümün Pythonic olacağını kabul ediyor:

"Ama bu bulmaca için önerilen herhangi bir çözümün karmaşıklığı benim için çok büyük: ayrıştırıcının (veya daha kesin olarak lexer) girintiye duyarlı ve girintiye duyarsız modlar arasında geçiş yapabilmesini ve bir yığın tutmasını gerektiriyor Teknik olarak bunların hepsi çözülebilir (zaten genelleştirilebilen bir yığın girinti seviyesi var.) Ancak bunların hiçbiri, hepsinin ayrıntılı bir Rube Goldberg ihlali olduğu hissini ortadan kaldırmaz . "


108
Bu neden en iyi cevap değil? Teknik nedenlerle ilgili değil, mucit tarafından açıkça belirtildiği gibi bir tasarım seçimi.
Dan Abramov

13
@ DanAbramov çünkü OP muhtemelen yıllarca giriş yapmadı.
Prof. Falken sözleşmesi

7
Rube Goldberg referansını anlamayanlar için bakınız: en.wikipedia.org/wiki/Rube_Goldberg_Machine
fjsj

56
Guido'nun cevabı, Python'un blokları tanımlamak için girintiye bağlı olmamasını dilememin başka bir nedeni.
LS

25
"Bağırsak hissi" diye bir tasarım seçeneği diyeceğime emin değilim. ;)
Elliot Cameron

54

Bu genellikle çok çirkin (ancak bazen alternatifler daha çirkin), bu nedenle bir çözüm, parantez ifadesi yapmaktır:

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

Yine de herhangi bir ödevi kabul etmeyecektir, bu nedenle önceden veri hazırlamak zorunda kalacaksınız. Bunu yararlı bulduğum yer, bazen kısa geri aramalarınız olan PySide sarıcı. Ek üye işlevleri yazmak daha da çirkin olacaktır. Normalde buna ihtiyacınız olmaz.

Misal:

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

2
Patronum PyQt uygulamamızda böyle bir şey istiyordu. Müthiş!
TheGerm

1
Bunun için teşekkürler, ben de PySide UI için geri arama olarak kısa (ama yine de çok satırlı) lambdas kullanmak için iyi bir yol arıyordu.
Michael Leonard

Ve şimdi bunu gördüm lambda argve setattr(arg, 'attr','value')"ödev yok ..." ı yıkmayı ve değiştirmeyi önerdi . Ve sonra kısa devre değerlendirmesi var andve or... bunu yapan Javascript. Kökleri sana batırıyor, sarmaşık gibi bir duvara batıyor. Umarım bunu Xmas üzerinde unuturum.
nigel222

oldukça akıllı - ve oldukça okunabilir. Şimdi - bu (eksik ..) ödevler hakkında ..
javadba

@ nigel222 neden utanıyorsun? Piton dili temelden sakat - ama kullanılan biri zaten büyük bir kısmında veri bilimi . Böylece ayarlamalar yapıyoruz. Yan etkiler (genellikle sadece yazdırma / günlük kaydı!) Ve ödevler (genellikle sadece ara değişkenler için yeterli!) Ancak desteklenmiyorlar (en azından PEPyönergeleri
izlerseniz

17

İlgili birkaç bağlantı:

Bir süre, başlangıçta Python'un Ruby blokları ile girintiye dayalı sözdizimini de Erlang'ın üstünde olacak olan Reia'nın gelişimini takip ediyordum. Ancak tasarımcı, girinti hassasiyetinden vazgeçti ve bu karar hakkında yazdığı bu yazı, girinti + çok satırlı bloklarla karşılaştığı sorunlar hakkında bir tartışma ve Guido'nun tasarım sorunları / kararları için artan bir takdir içeriyor:

http://www.unlimitednovelty.com/2009/03/indentation-sensitivity-post-mortem.html

Ayrıca, burada Guido'nun gerçekten vurmadan bir yanıt yayınladığı Python'da Ruby tarzı bloklar için ilginç bir teklif var (yine de bir sonraki vurma olup olmadığından emin değilim):

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


12

[Düzenle] Bu yanıtı okuyun . Çok satırlı lambda'nın neden bir şey olmadığını açıklar.

Basitçe söylemek gerekirse, bu unpythonic. Guido van Rossum'un blog gönderisinden:

Bir ifadenin ortasına girinti tabanlı bir blok yerleştiren herhangi bir çözümü kabul edilemez buluyorum. İfade gruplaması için alternatif sözdizimi bulduğumdan (örneğin parantez veya başlangıç ​​/ bitiş anahtar kelimeleri) eşit derecede kabul edilemez olduğundan, bu çok satırlı bir lambda'yı çözülemez bir bulmaca haline getirir.


10

Size şanlı ama korkunç bir hack sunmama izin verin:

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)

Artık bu LETformu şu şekilde kullanabilirsiniz :

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

hangi verir: [0, 3, 8]



Bu harika! Sanırım bunu bir sonraki Python yazışımda kullanacağım. Ben öncelikle bir Lisp ve JS programcısıyım ve çok hatlı lambada eksikliği acıyor. Bunu elde etmenin bir yolu bu.
Christopher Dumas

7

Biraz daha basit olan bazı projelerimde bu kirli hack'i uygulamaktan suçluyum:

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

Umarım pitonik kalmanın bir yolunu bulabilirsiniz ama bunu yapmak zorundaysanız, exec ve globalleri manipüle etmekten daha az acı verici.


6

@ Balpha ayrıştırma sorununu çözmeye çalışayım. Çok satırlı lamda çevresinde parantez kullanırdım. Parantez yoksa lambda tanımı açgözlüdür. Yani lambda içeri

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

döndüren bir işlev döndürür (y*z, [1,2,3])

Fakat

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

anlamına geliyor

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

burada işlev y * z döndüren çok satırlı lambdadır. İşe yarıyor mu?


1
map(func, [1,2,3])Harita işlevinin yeterli argümanı olmadığı için en üstte dönmesi ve en altta bir hata olması gerektiğini düşünüyorum. Ayrıca kodda bazı ekstra parantezler vardır.
Samy Bencherif

bunu python2.7.13 çalıştıran pycharm'a bırakmak sözdizimi hatası verir.
simbo1905

ekstra parantez
Samy Bencherif

4

(Konuya hala ilgi duyan herkes için.)

Bunu düşünün ("çok satırlı" lambda içindeki diğer ifadelerde ifadelerin dönüş değerlerinin eşit kullanımını içerir, ancak kusma noktasına kadar çirkin ;-)

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

Bu, farklı parametrelerle ikinci kez çağrıldığında çalışmaz ve ilk satır state.clear()varsayılan işlevler yalnızca işlev oluşturulduğunda bir kez oluşturulduğu için bellek sızıntısına neden olur .
Matthew D. Scholefield

1

\Lambda işleviniz için birden fazla satırınız varsa slash ( ) yöntemini kullanabilirsiniz .

Misal:

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

Output: 30

Soru, 1'den fazla değişmez satır yerine 1'den fazla ifade kullanmakla ilgilidir.
Tomas Zubiri

1

Python ile başlıyorum ama Javascript gelen en belirgin yolu bir işlev olarak ifade ayıklamak olduğunu ....

Onaylanmış örnek, çarpma ifadesi (x*2)işlev olarak çıkarılır ve bu nedenle çok satırlı kullanabilirsiniz:

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-function

Belki de lambda ifadesinin kendisinde çok satırlı yapılıp yapılmadığı sorusuna tam olarak cevap vermez , ancak birisinin bu iş parçacığını (benim gibi) ifadeyi nasıl ayıklayacağına bakması durumunda yardımcı olacağını düşünüyorum


2
Bunu neden yazayım ki yazmıyorum map(multiply, [1, 2, 3])?
thothal

0

Çirkin hack'lerde, böyle execçok satırlı bir işlevi tanımlamak için her zaman bir kombinasyonunu ve normal bir işlevi kullanabilirsiniz:

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

Bunu aşağıdaki gibi bir işleve sarabilirsiniz:

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

Ben sadece azaltmak ile dik bir anlayış yapmak için biraz oynuyordum ve bu bir liner hack ile geldim:

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}

Ben sadece bu Javascript dict kavrama yapılanla aynı yapmaya çalışıyordum: https://stackoverflow.com/a/11068265


0

Çok satırlı lambdaların daha ilginç bir uygulaması. Python'un, kodu yapılandırmanın bir yolu olarak girintileri nasıl kullandığı nedeniyle elde etmek mümkün değildir.

Ancak neyse ki bizim için girinti biçimlendirme diziler ve parantez kullanılarak devre dışı bırakılabilir.

Bazılarının belirttiği gibi, kodunuzu şöyle yazabilirsiniz:

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

Teoride, soldan sağa değerlendirme yapacağınız garanti edilirse işe yarayacaktır, ancak yine de bir ifadeden diğerine aktarılan değerleri kaybedersiniz.

Biraz daha ayrıntılı olana ulaşmanın bir yolu,

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

Her lambda bir öncekinden argümanlar alır.

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

Bu yöntem, biraz lisp / şema gibi bir şey yazmanıza izin verir.

Böylece böyle şeyler yazabilirsiniz:

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

Hipotenüsü hesaplamak için daha karmaşık bir yöntem kullanılabilir

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

Bu, skaler sayıların bir listesini döndürür, böylece birden çok değeri bire düşürmek için kullanılabilir.

Birçok lambdaya sahip olmak kesinlikle çok verimli olmayacak, ancak kısıtlıysanız, hızlı bir şekilde bir şey yapmanın iyi bir yolu olabilir, daha sonra gerçek bir işlev olarak yeniden yazabilirsiniz.


-3

çünkü lambda fonksiyonunun tek sıralı olması gerekiyordu, çünkü bir fonksiyonun en basit şekli, an entrance, then return


1
Hayır, olay odaklı bir program yazan herkes size çok satırlı lambda'nın önemli olduğunu söyleyecektir.
Akangka
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.