Birisi BDFL'nin Python lambdas tek satır yapmayı seçmesinin somut nedenlerini açıklayabilir mi?
Bu iyi:
lambda x: x**x
Bu bir hatayla sonuçlanır:
lambda x:
x**x
Lambda çok satırlı yapmanın bir şekilde normal girinti kurallarını "rahatsız edeceğini" ve daha fazla istisna eklemeyi gerektirdiğini anlıyorum, ancak faydalara değmez mi?
Örneğin JavaScript'e bakın. Kişi bu anonim işlevler olmadan nasıl yaşayabilir? Onlar vazgeçilmezler. Pythonistas, sadece bir argüman olarak geçmek için her çok satırlı işlevi adlandırmaktan kurtulmak istemiyor mu?
Guido van van Rossum buna cevap verdi:
Ancak bu tür çözümler genellikle "Pythonicity" ye sahip değildir - iyi bir Python özelliğinin bu zor özelliği. Pythonicity'yi zor bir kısıtlama olarak ifade etmek imkansızdır. Python Zen'i bile basit bir Pythonicity testine dönüşmez ...
Yukarıdaki örnekte, önerilen çözümün Aşil topuğunu bulmak kolaydır: çift kolon, sözdizimsel olarak belirsiz olsa da ("bulmaca kısıtlamalarından" biri), tamamen keyfidir ve Python'da başka hiçbir şeye benzemez ...
Ama bunu da reddediyorum, çünkü sonunda (ve kasten istemciyi yanıltıcı olarak kabul ettiğim yer), 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 (ör. 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.
http://www.artima.com/weblogs/viewpost.jsp?thread=147358
Temel olarak, bir çözüm mümkün olmasına rağmen, bunun Python ile uyumlu olmadığını söylüyor.
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ğidir; anahtar kelime içeremez (python2'nin print
veya return
gibi).
GvR bunu normalde parametre olarak kullanıldığından lambda'nın boyutunu sınırlamayı seçer. Gerçek bir işlev istiyorsanız, def
kullanın
Bunun çok eski olduğunu biliyorum, ama buraya referans olarak.
Lambda kullanmanın bir alternatifi, konvansiyonel olmayan bir şekilde bir def
kullanmak olabilir. Amaç sadece bir durumda yapılabilecek bir işleve def
iletmek - bir dekoratör. Bu uygulama ile def result
İşlev oluşturmazsa, reduce()
sonucunu oluşturur ve sonuçta dict
olur.
Utanmaz fiş : Bunu çok yapıyorum burada .
>>> 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 deyimli lambdas can ancak sadece gerçekten çirkin bir kodla yapılabilir. Ancak ilginç olan, kapsam belirlemenin bu uygulama ile nasıl çalıştığıdır (name
değişkeninin çoklu kullanımına ve message
değişkeninin gölgelenmesine dikkat edin.
>>> 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!
Çok ifadeli bir lambda'yı birlikte kesmek, pyrospade'nin ortaya çıkardığı kadar kötü değil: eminim ki gibi bağlama kullanarak bir grup monadik fonksiyon oluşturabiliriz. Haskell, ama Python'un saf dünyasında olduğumuzdan, aynı şeyi elde etmek için yan etkileri de kullanabiliriz.
Bunu blogum üzerinde yapmanın birkaç yolunu ele alıyorum.
Örneğin, Python bir Tuple öğesinin öğelerini sırayla değerlendirmeyi garanti eder, bu nedenle ,
'Yu zorunlu bir ;
Gibi kullanabiliriz. , print
gibi, ifadelerle sys.stdout.write
gibi.
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]
Sonuna None
eklediğimi ve [-1]
Kullanarak çıkarttığımı unutmayın; bu dönüş değerini açıkça ayarlar. Bunu yapmak zorunda değiliz, ama onsuz umursadığımız veya umursamayacağımız bir funky (None, None, None)
Dönüş değeri elde ederiz.
Yani IO eylemleri sıralayabiliriz. Peki ya yerel değişkenler?
Python'un =
İfadesini oluşturur, bu yüzden eşdeğer bir ifade bulmamız gerekir. Bir yol, bir argüman olarak aktarılan 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])({})
stateful_lambda
İçinde az sayıda püf noktası var:
*_
Argümanı lambdamızın herhangi bir sayıda argüman almasına izin verir. Bu, sıfır argümanlara izin verdiğinden, stateful_def
Çağırma kuralını kurtarıyoruz. _
Argümanını çağırmak sadece "Bu değişkeni kullanmayacağım" diyen bir kuraldır.lambda state: lambda *_: ...
(lambda state: ...)({})
{}
state
değişkenini atamamıza izin verir (örn. state = {}
)state
içindeki anahtarları ve değerleri değişken adları ve ilişkili değerler Olarak ele alıyoruza = b
Yerine state.setdefault(a, b)
ve a
yerine state.get(a)
kullanıyoruzreturn
ifadesi gibi davranan son değeri ayıklamak için [-1]
KullanıyoruzTabii ki bu oldukça hantal, ancak yardımcı işlevlerle daha güzel 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])
Ben katkıda bulunabilir rağmen, bir satır kesici kullanın:
x = lambda x,y: x-y if x<y \
else y-x if y<x \
else 0
python oneliner yazabilen çok güzel bir şeyi unutmayın, örneğin:
a=b=0; c=b+a; d = a+b**2 #etc etc
Ve lambda çok güçlüdür, ancak 1 tam fonksiyonun yerine geçmesi amaçlanmamıştır, yani bunu hackleyebileceğiniz anlamına gelir (yukarıdaki meslektaşımdan borçlanma örneği):
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 bunu gerçekten böyle yapmak istiyor musun? Bir süre sonra çoğunlukla okunamaz, sökülmemiş uçtan başlayarak ipin başlangıcına ulaşmak gibidir.
Lambdalar, Fonksiyonel Odaklı Programlama'da (diğer şeylerin yanı sıra) harita, filtre ve azaltma fonksiyonlarında tek bir fonksiyon olarak ifade edilir. Örneğin, tamsayı olan ve 2'ye bölünebilen değerlerin karakter değerlerini almak
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]
Karmaşık işlevi (veya daha fazla ve birkaç lambda'yı ayırarak ve başka bir lambda içine koyarak işlev ifadeleri işlevi olarak kullanabilirsiniz:
def someAnon(*args): return sum(list(args))
defAnon = lambda list: [ x*someAnon(*list) for x in list]
ancak Python işlev ifadeleri desteği başka bir şekilde var: -lets superAwesomeFunction
adlı bir işleve sahip olduğunuzu ve bu işlevin süper harika şeyler yapabileceğini söylüyor, bir değişkene atayabilirsiniz bu şekilde arayarak:
SAF = superAwesomeFunction # there is no () at the end,
Şimdi SAF'ı aradığınızda superAwesomeFunction veya yöntemini çağırırsınız. Lib klasörünüzde arama yaparsanız, python __builtin__
Modüllerinin çoğunun bu şekilde yazıldığını görebilirsiniz.Bunu yaptığınız için bazen belirli görevleri yerine getiren bazı işlevlere ihtiyacınız olacaktır kullanıcı tarafından kullanılabilir olmak için yeterli değildir, ancak birkaç işlev için gereklidir.Bu nedenle "superAwesomeFunction" adında 2 işleviniz olamaz, "superAwesomeFunctionDoingBasicStuf" ve "realSuperAwesomeFunction" olabilir ve sadece koymak "superAwesomeFunction" değişkeni "realSuperAwesomeFunction" ve bitirdiniz.
İçe aktarılan modüllerin konumunu, konsola importedModule.__file__
(Gerçek örnek import os;os.__file__
) Girerek bulabilirsiniz ve importModule.py ve editörde açın ve kendi "bilginizi" nasıl en üst düzeye çıkarabileceğinizi bulun.
Umarım bu size ve belki de sorun yaşayan diğer meslektaşlarınıza yardımcı olur.