Python neden çok hatlı lambdalara izin vermiyor?


50

Birisi BDFL'nin Python lambdas'ı tek satır yapmak için seçtiği somut nedenleri açıklayabilir mi?

Bu iyi:

lambda x: x**x

Bu bir hataya yol açar:

lambda x:
    x**x

Lambda çok satırlı yapmanın normal girinti kurallarını bir şekilde "rahatsız edeceğini" ve daha fazla istisna eklemeyi gerektirdiğini, ancak bunun faydalarına değmeyeceğini anlıyorum.

Örneğin, JavaScript’e bakın. Bu isimsiz işlevler olmadan nasıl yaşayabiliriz? Onlar vazgeçilmezdir. Pythonistas her çok satırlı işlevi isimlendirmek için sadece argüman olarak adlandırmak zorunda kalmaktan kurtulmak istemez mi?


3
Guido'nun çoklu ifadeli lambdalara izin vermemesinin somut nedenlerini not edip onları reddettiğinizi düşününce, gerçek bir cevaptan ziyade doğrulama istediğinizi varsayacağım.
Jason Baker

3
Yedi karakter kaydetmenin yanı sıra, bu bir karakterden daha iyi nasıl olabilir def? Şimdi tamamen aynı görsel yapıya sahip.
01’de:

Yanıtlar:


42

Guido van van Rossum bunu yanıtladı:

Ancak bu tür çözümler genellikle "Pythonicity" den yoksun - iyi bir Python özelliğinin zorlu özelliği. Pythonicity'yi zor bir kısıtlama olarak ifade etmek imkansızdır. Python'un Zen'i bile basit bir Pythonicity sınavına çevrilmiyor ...

Yukarıdaki örnekte, önerilen çözümün Aşil topuğunu bulmak kolaydır: çift kolon, sözdizimsel olarak kesin olmasa da ("bulmaca kısıtlamalarından" biri) tamamen keyfidir ve Python'da başka hiçbir şeye benzemez ...

Ancak bunu da reddediyorum, çünkü sonunda (ve bu, istemeden yanıltıcıyı yanlış yönlendirdiğimi itiraf ettiğim yer), bir ifadenin ortasındaki bir girintiye dayalı bloğu yerleştiren kabul edilemez herhangi bir çözüm buluyorum. İfade gruplaması için alternatif sözdizimi bulduğum için (örneğin, parantez veya başlangıç ​​/ son anahtar kelimeler) eşit derecede kabul edilemez, bu çok satırlı bir lambda'yı çözülemez bir yapboz yapıyor.

http://www.artima.com/weblogs/viewpost.jsp?thread=147358

Temel olarak, her ne kadar bir çözüm mümkün olsa da, Python'un ne kadar uygun olmadığını söylüyor.


2
İş parçacığı bağlantısı için +1 teşekkürler - ama yine de çok satırlı lambdalar için minnettar olurum - paha biçilmezler - JavaScript'e bak, PHP onları da içeriyor.
treecoder

2
@greengit Yalnızca iç içe bir def kullanabilirsiniz. Anonim işlevlerle aynı değildir, ancak yeterince yakındırlar.
jsternberg

2
iç içe geçmiş def işlevler argüman olarak işlevlerini geçerken yardımcı olmuyor - bu çok satırlı kuzuları tercih etmemin bir numaralı nedeni
treecoder

1
@greengit - Bunu GvR ile almaktan, yorumlarınızı burada yayınlamaktan daha iyi olduğunu düşünüyorum.
Jason Baker,

9
@greengit: İşlevi başka bir işleve argüman olarak iletebileceğinizi biliyor musunuz? Satır içi yazamazsınız, ancak sizin için mevcut olmayan bir programlama tekniği yoktur.
btilly

24

python'da çok satırlı bir lambda yapmak gayet iyi: bkz.

>>> f = lambda x: (
...   x**x)
>>> f
<function <lambda> at 0x7f95d8f85488>
>>> f(3)
27

gerçek lambda sınırlaması, lambda'nın tek bir ifade olması gerektiği gerçeğidir ; anahtar kelime içeremez (python2'ler printveya gibi return).

GvR lambda boyutunu sınırlamak için bunu seçer, çünkü normalde parametre olarak kullanılırlar. Gerçek bir işlev istiyorsanız, kullanın.def


1
çoklu çizgi '\ n' karakterinin eklenmesiyle ilgilidir: D python çoklu ifade lambda'sına sahip değil . Gerçekten kullanmak istiyorsun def. Bir düşünün: fonksiyonunuzun bir parametresi olarak gerçekten bir cihaza ihtiyacınız var mı? Ve bu işlevin kullanıcıların geçmesine izin verilmez sizin varsayılan çağrılabilir? Onlara vermezseniz, nasıl geçebilirler?
Vito De Tullio

btw, anonim bir fonksiyona ihtiyaç duyduğunuza dair bir örnek verebilir misiniz?
Vito De Tullio

1
Evet, tek bir ifadenin sınırlandırılmasını gerçekten sinir bozucu buluyorum. Doğru, eğer çok ifadeli lambdalara izin verirlerse, insanlar kesinlikle onu kötüye kullanmaya başlayacaklar, ancak bunun tersi de çok kısıtlayıcı.
rbaleksandar

10

Bunun süper eski olduğunu biliyorum, ama buraya referans olarak koyuyorum.

Lambda kullanmanın bir alternatifi, alışılmadık defolmayan bir şekilde kullanılması olabilir. Amaç, defsadece bir durumda yapılabilecek bir işleve geçmek - bir dekoratör. Bu uygulama ile def resultbir işlev yaratmadığına, bunun bir sonuç olduğu sonucuna reduce()vardığına dikkat edin dict.

Utanmaz fiş : Burada çok şey yapıyorum .

>>> xs = [('a', 1), ('b', 2), ('a', 3), ('b', 4)]
>>> foldl = lambda xs, initial: lambda f: reduce(f, xs, initial)
>>> @foldl(xs, {})
... def result(acc, (k, v)):
...     acc.setdefault(k, 0)
...     acc[k] += v
...     return acc
...
>>> result
{'a': 4, 'b': 6} 

Çok deyimi lambda'lar unutmayın olabilir , ama sadece gerçekten birlikte, yapılması gerçekten çirkin kodu. Ancak, ilginç olan, kapsam belirleme işleminin bu uygulamayla nasıl çalıştığıdır ( namedeğişkenin çoklu kullanımına ve değişkenin gölgesine dikkat edin) message.

>>> from __future__ import print_function
>>> bind = lambda x, f=(lambda x: x): f(x)
>>> main = lambda: bind(
...     print('Enter your name.'), lambda _: bind(
...     raw_input('> '), lambda name: bind(
...     'Hello {}!'.format(name), lambda message: bind(
...     print(message), lambda _: bind(
...     'Bye {}!'.format(name), lambda message: bind(
...     print(message)
... ))))))
>>> main()
Enter your name.
> foo
Hello foo!
Bye foo!


Monad'lara ayrıca zamanlanabilirler veya gelecek / vaatler, hatta JavaScript BTW'deki geri aramalar denir.
aoeu256

3

Çoklu ifadeli bir lambdayı bir araya getirmek, pyrospade'ın ortaya çıkardığı kadar kötü değildir: Haskell'deki gibi, bind kullanarak bir sürü monadik işlev oluşturabileceğimize emin olabiliriz , ancak Python'un saf dünyasında olduğumuzdan, biz de yapabiliriz. aynı şeyi başarmak için yan etkileri kullanın.

Ben bunu yapmak için birkaç yol kapsayacak bloguma .

Örneğin, Python bir tuple öğelerini sırayla değerlendirmeyi garanti eder, bu yüzden ,bir zorunluluk gibi kullanabilirsiniz ;. Biz gibi birçok ifadeleri yerini alabilecek printgibi ifadelerle, sys.stdout.write.

Dolayısıyla aşağıdakiler eşdeğerdir:

def print_in_tag_def(tag, text):
    print "<" + tag + ">"
    print text
    print "</" + tag + ">"

import sys
print_ = sys.stdout.write
print_in_tag_lambda = lambda tag, text: (print_("<" + tag + ">"),
                                         print_(text),
                                         print_("</" + tag + ">"),
                                         None)[-1]

NoneSonunda bir tane eklediğimi ve onu kullanarak çıkardığımı not edin [-1]; Bu, dönüş değerini açıkça ayarlar. Bunu yapmak zorunda değiliz, ancak onsuz (None, None, None)umursamayacağımız ya da umursamayacağımız korkak bir geri dönüş değeri elde ederiz.

Böylece GÇ işlemlerini sıralayabiliriz. Peki ya yerel değişkenler?

Python's =bir ifade oluşturur, bu yüzden eşdeğer bir ifade bulmalıyız. Bir yol, argüman olarak iletilen veri yapısının içeriğini değiştirmektir. Örneğin:

def stateful_def():
    foo = 10
    bar = foo * foo
    foo = 2
    return foo + bar

stateful_lambda = (lambda state: lambda *_: (state.setdefault('foo', 10),
                                             state.setdefault('bar', state.get('foo') * state.get('foo')),
                                             state.pop('foo'),
                                             state.setdefault('foo', 2),
                                             state.get('foo') + state.get('bar'))[-1])({})

Kullanılan birkaç püf noktası vardır stateful_lambda:

  • *_Argüman bizim lambda almasına izin veriyor herhangi argümanların sayısını. Bu, sıfır argümanlarına izin verdiğinden , arama çağrışımını kurtarırız stateful_def.
    • Bir argüman çağırmak _sadece "Bu değişkeni kullanmayacağım" yazan bir kongredir.
  • Başka bir ("ana") işlev döndüren bir ("sarmalayıcı") işlevimiz var: lambda state: lambda *_: ...
    • Sözcük kapsamı sayesinde , ilk işlevin argümanı ikinci işlevin kapsamı içinde olacaktır
    • Bazı argümanları şimdi kabul etmek ve gerisini daha sonra kabul etmek için başka bir işlevi döndürmek, körleme olarak bilinir.
  • Hemen "sarmalayıcı" işlevini çağırır, boş bir sözlük geçiririz: (lambda state: ...)({})
    • Bu, bir atama ifadesi kullanmadan bir değişkene statebir değer {}atamamızı sağlar (örn. state = {}).
  • Anahtarlara ve değerlere statedeğişken isimleri ve sınır değerleri olarak bakarız.
    • Bu hemen denilen lambdaları kullanmaktan daha az hantaldır.
    • Bu, değişkenlerin değerlerini değiştirmemizi sağlar
    • Biz kullanmak state.setdefault(a, b)yerine a = bve state.get(a)yerinea
  • Daha önce olduğu gibi yan etkilerimizi bir araya getirmek için bir zincir kullanıyoruz.
  • [-1]Bir returnifade gibi davranan son değeri çıkarmak için kullanırız

Tabii ki bu oldukça hantal, ama yardımcı fonksiyonlarla daha iyi bir API yapabiliriz:

# Keeps arguments and values close together for immediately-called functions
callWith = lambda x, f: f(x)

# Returns the `get` and `setdefault` methods of a new dictionary
mkEnv = lambda *_: callWith({},
                            lambda d: (d.get,
                                       lambda k, v: (d.pop(k), d.setdefault(k, v))))

# A helper for providing a function with a fresh `get` and `setdefault`
inEnv = lambda f: callWith(mkEnv(), f)

# Delays the execution of a function
delay = lambda f x: lambda *_: f(x)

# Uses `get` and `set`(default) to mutate values
stateful_lambda = delay(inEnv, lambda get, set: (set('foo', 10),
                                                 set('bar', get('foo') * get('foo')),
                                                 set('foo', 2),
                                                 get('foo') + get('bar'))[-1])

dalga mı geçiyorsun, bu bir kabus lol gibi görünüyor
Alexander Mills

1
@AlexanderMills Heh, gerçek dünyadan bir örnek değil, daha çok pyrospade'ın lambda lambdalarını lambda yaklaşımını reddetti, işlerin o kadar da kötü olmadığını gösterdi . Aslında, bu şimdi daha da basitleştirilebilirdi, python.org/dev/peps/pep-0572
Warbo

1

Katkıda bulunabildiğim halde satır kırıcı kullanıyorum:

x = lambda x,y: x-y if x<y \ 
                     else y-x if y<x \
                     else 0

Python'un oneliners yazabileceği en güzel şeyi, örneğin:

a=b=0; c=b+a; d = a+b**2 #etc etc

Ve lambda çok güçlüdür, ancak 1 bütün fonksiyonun yerine geçmek için tasarlanmamıştır, demek istediğin gibi kesebilirsin (yukarıdaki meslektaştan örnek ödünç alıyorsun):

makeTag = lambda tagName: "<{}>".format(tagName)
closeTag = lambda tagName: makeTag("/"+str(tagName))
openTag = lambda tagName: makeTag(tagName)
writeHMTLline = lambda tag,content: ""+opetTag(tag)+str(content)+closeTag(tag)

Ama gerçekten böyle yapmak istiyor musun? Bir süre sonra çoğunlukla okunamaz durumdadır, ipin çözülmemiş ucu ile başlayan başlangıcına ulaşmak gibidir. çözülmüş ip

Lambdalar, sadece bir fonksiyon olarak ele alınır, harita üzerinde, Fonksiyonel Yönelimli Programlamada (diğer şeylerin yanı sıra) fonksiyonları filtreler ve azaltır. Örneğin, tamsayı olan ve 2 ile bölünebilen değerlerin karakter değerlerini alma

chrDev2 = lambda INT: chr(INT) if isinstance(INT,int) and INT%2==0 else INT
someStringList = map( chrDev2, range(30) )
>>> ['\x00', 1, '\x02', 3, '\x04', 5, '\x06', 7, '\x08', 9, '\n', 11, '\x0c', 13, '\x0e', 15, '\x10', 17, '\x12', 19, '\x14', 21, '\x16', 23, '\x18', 25, '\x1a', 27, '\x1c', 29]

İşlev ifadeleri karmaşık işlev tanımlayarak (veya daha fazla ve birkaç lambda kullanarak ve başka bir lambda koyarak) kullanabilirsiniz.

def someAnon(*args): return sum(list(args))
defAnon = lambda list: [ x*someAnon(*list) for x in list]

ancak Python'un fonksiyon ifadeleri başka bir şekilde de desteklemektedir: -let'ler, çağrılan bir işleve sahip superAwesomeFunctionolduğunuzu ve bu işlevin bazı süper harika şeyler yapabileceğini söyler, şöyle bir değişkeni atayamazsınız, şöyle:

SAF = superAwesomeFunction # there is no () at the end, 

Yani şimdi SAF'ı aradığınızda superAwesomeFunction veya yöntemini çağıracaksınız. Lib klasörünüzde arama yaparsanız, python __builtin__modüllerinin çoğunun bu şekilde yazıldığını görebilirsiniz. Bu yapılır, çünkü bazen kullanıcı tarafından kullanılabilecek kadar gerekli olmayan, belirli bir görevi yerine getiren bazı işlevlere ihtiyaç duyacaksınız, ancak bu birkaç işlev için gereklidir. O zaman "superAwesomeFunction" adında 2 işleve sahip olamayacaksınız, "superAwesomeFunctionDoingBasicStuf" ve "realSuperAwesomeFunction" seçeneğine sahip olabilirsiniz ve "superAwesomeFunction" değişkenini "realSuperAwesomeFunction" yerine koyarsınız ve bitirdiniz.

Sen konsolunda girerek ithal edilen modüllerin yerini bulabilir importedModule.__file__(gerçek örnek import os;os.__file__), ve sadece bu dizin denilen dosyası için takip importedModule.py ve editörü açın ve kendi "bilgi" maksimize nasıl bulmak.

Umarım bu size ve belki de diğer meslektaşlarına dertte olur.

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.