it-swarm.asia

Python çok satırlı lambdalara neden izin vermiyor?

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?

53
treecoder

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.

46
BlackJack

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

25
Vito De Tullio

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!
10
pyrospade

Ç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.
  • Başka bir ("ana") işlev döndüren bir ("sarmalayıcı") işlevimiz var: lambda state: lambda *_: ...
    • sözcüksel kapsam sayesinde, ilk işlevin argümanı ikinci işlev için kapsamda olacaktır
    • Bazı argümanları şimdi kabul etmek ve geri kalanını daha sonra kabul etmek için başka bir işlev döndürmek currying olarak bilinir
  • Hemen "sarmalayıcı" işlevini boş bir sözlükle geçiyoruz: (lambda state: ...)({})
    • Bu, atama ifadesi kullanmadan bir değere {}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ıyoruz
    • Bu, hemen lambdas olarak adlandırılandan daha az kullanışlıdır
    • Bu, değişkenlerin değerlerini değiştirmemizi sağlar
    • a = b Yerine state.setdefault(a, b) ve a yerine state.get(a) kullanıyoruz
  • Daha önce olduğu gibi yan etkilerimizi zincirlemek için bir Tuple kullanıyoruz
  • return ifadesi gibi davranan son değeri ayıklamak için [-1] Kullanıyoruz

Tabii 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])
4
Warbo

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. unraveled rope

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.

1
Danilo