Lambda'da otomatik tuple paketinden çıkarma için iyi python3 eşdeğeri nedir?


100

Aşağıdaki python2 kodunu düşünün

In [5]: points = [ (1,2), (2,3)]

In [6]: min(points, key=lambda (x, y): (x*x + y*y))
Out[6]: (1, 2)

Bu python3'te desteklenmiyor ve aşağıdakileri yapmam gerekiyor:

>>> min(points, key=lambda p: p[0]*p[0] + p[1]*p[1])
(1, 2)

Bu çok çirkin. Lambda bir işlev olsaydı, yapabilirdim

def some_name_to_think_of(p):
  x, y = p
  return x*x + y*y

Python3'te bu özelliğin kaldırılması, kodu ya çirkin bir şekilde (sihirli indekslerle) yapmaya ya da gereksiz işlevler oluşturmaya zorlar (En rahatsız edici kısım, bu gereksiz işlevler için iyi isimler düşünmektir)

Özelliğin en azından tek başına lambdalara geri eklenmesi gerektiğini düşünüyorum. İyi bir alternatif var mı?


Güncelleme: Cevapta fikri genişleten aşağıdaki yardımcıyı kullanıyorum

def star(f):
  return lambda args: f(*args)

min(points, key=star(lambda x,y: (x*x + y*y))

Güncelleme2: Daha temiz bir sürümstar

import functools

def star(f):
    @functools.wraps(f):
    def f_inner(args):
        return f(*args)
    return f_inner

4
Muhtemelen lambdadilden tamamen çıkarılma olasılığı daha sonra, kullanımı zorlaştıran değişiklikleri tersine çevirmek daha olasıdır , ancak özelliğin geri eklendiğini görme isteğinizi ifade etmek istiyorsanız python fikirlerini yayınlamayı deneyebilirsiniz.
Wooble

3
Ben de alamadım ama BDFL karşı gibi görünüyor lambdao karşı aynı ruhla map, reduceve filter.
Cuadue

3
lambdatemelde dilde bir sorun olduğu için py3k'de kaldırılması planlandı. Ancak anonim işlevleri tanımlamak için uygun bir alternatif üzerinde kimse anlaşamadı, bu yüzden sonunda Guido yenilgiye uğradı ve bu oldu.
roippi

5
anonim işlevler herhangi bir uygun dilde olmalıdır ve lambdaları çok beğeniyorum. Böyle bir tartışmanın nedenlerini okumam gerekecek. (Ayrıca, anlama gelse de mapve filteren iyisi anlama gelse de, severim reduce)
njzk2

13
Python 3 hakkında sevmediğim tek şey ...
timgeb

Yanıtlar:


35

Hayır, başka yolu yok. Hepsini kapladın. Gitmenin yolu, bu sorunu Python fikirleri posta listesinde gündeme getirmek olabilir, ancak biraz ilgi kazanmak için orada çok tartışmaya hazır olun.

Aslında, "çıkış yolu yok" dememek için üçüncü bir yol, yalnızca parametreleri açmak için bir seviye daha lambda çağrısı uygulamak olabilir - ancak bu, iki önerinizden daha verimsiz ve okunması daha zor olacaktır:

min(points, key=lambda p: (lambda x,y: (x*x + y*y))(*p))

Python 3.8'i güncelle

Şu an itibariyle, Python 3.8 alpha1 mevcuttur ve PEP 572- atama ifadeleri uygulanmaktadır.

Bu yüzden, eğer biri bir lambda içinde birden fazla ifadeyi çalıştırmak için bir numara kullanırsa - bunu genellikle bir demet oluşturarak ve sadece son bileşenini döndürerek yaparım, bunu yapmak mümkündür:

>>> a = lambda p:(x:=p[0], y:=p[1], x ** 2 + y ** 2)[-1]
>>> a((3,4))
25

Bu tür bir kodun nadiren tam bir işleve sahip olmaktan daha okunaklı veya pratik olacağı unutulmamalıdır. Yine de, olası kullanımlar vardır - eğer bunun üzerinde çalışacak çeşitli tek pointsatırlılar varsa, adlandırılmış bir gruba sahip olmak ve atama ifadesini, gelen diziyi adlandırılmış tekil gruba etkili bir şekilde "dönüştürmek" için kullanmak faydalı olabilir:

>>> from collections import namedtuple
>>> point = namedtuple("point", "x y")
>>> b = lambda s: (p:=point(*s), p.x ** 2 + p.y ** 2)[-1]

3
Ve elbette, bunu yapmanın "tek ve açık" yolunun def, nme altındaki soruda bahsedilen üç satırlı işlevi kullanarak gerçekten harcamak olduğunu söylemeyi unuttum some_name_to_think-of.
jsbueno

2
Bu cevap hala bazı ziyaretçileri görüyor - PEP 572 uygulamasıyla, lambda ifadesinin içinde değişkenler oluşturmanın bir yolu olmalı, örneğin:key = lambda p: (x:=p[0], y:=p[1], x ** 2 + y ** 2)[-1]
jsbueno

1
atama ifadeleri !!
başardıklarına

24

Http://www.python.org/dev/peps/pep-3113/ e göre tuple paket açma işlemi gitti ve 2to3bunları şu şekilde çevirecek:

Tuple parametreleri, tek ifade sınırlaması nedeniyle lambdalar tarafından kullanıldığından, bunların da desteklenmeleri gerekir. Bu, beklenen sıra bağımsız değişkeninin tek bir parametreye bağlanması ve ardından bu parametrenin dizine alınmasıyla yapılır:

lambda (x, y): x + y

şu dile çevrilecektir:

lambda x_y: x_y[0] + x_y[1]

Sizin uygulamanıza oldukça benzer.


3
Döngü veya kavrayışlar için kaldırılmaması iyi
balki

12

Python 2 argümanlarının paket açma davranışına iyi bir genel alternatif bilmiyorum. İşte bazı durumlarda faydalı olabilecek birkaç öneri:

  • aklınıza bir isim gelmiyorsa; anahtar kelime parametresinin adını kullanın:

    def key(p): # more specific name would be better
        x, y = p
        return x**2 + y**3
    
    result = min(points, key=key)
    
  • namedtupleliste birden çok yerde kullanılıyorsa, a kodunuzu daha okunabilir hale getirip getirmediğini görebilirsiniz :

    from collections import namedtuple
    from itertools import starmap
    
    points = [ (1,2), (2,3)]
    Point = namedtuple('Point', 'x y')
    points = list(starmap(Point, points))
    
    result = min(points, key=lambda p: p.x**2 + p.y**3)
    

Bu sorunun şimdiye kadarki tüm cevapları arasında, adlandırılmış bir ikili kullanmak buradaki en iyi eylemdir. Sadece size lambda tutabilir ama aynı zamanda ben namedtuple versiyonu arasındaki fark olarak daha okunabilir bulmak lambda (x, y):ve lambda x, y:ilk bakışta açık değildir.
Burak Arslan

5

Python3'te yıkıcı argümanlar kaldırılırken, anlamalardan çıkarılmadı. Python 3'te benzer bir davranış elde etmek için kötüye kullanmak mümkündür. Özünde, ortak rutinlerin fonksiyonları tersine çevirmemize izin verdiği ve verimin bir ifade olmadığı ve dolayısıyla lambdas içinde izin verildiği gerçeğinden yararlanıyoruz.

Örneğin:

points = [(1,2), (2,3)]
print(min(points, key=lambda y: next(x*x + y*y for x,y in (lambda a: (yield a))(y))))

Bir sarmalayıcı kullanmanın kabul edilen yanıtıyla karşılaştırıldığında, bu çözüm, sarmalayıcı yalnızca birinci düzeyi yok ederken, argümanları tamamen yok edebilir. Yani,

values = [(('A',1),'a'), (('B',0),'b')]
print(min(values, key=lambda y: next(b for (a,b),c in (lambda x: (yield x))(y))))

Kıyasla

values = [(('A',1),'a'), (('B',0),'b')]
print(min(points, key=lambda p: (lambda a,b: (lambda x,y: (y))(*a))(*p)))

Alternatif olarak bir de yapabilir

values = [(('A',1),'a'), (('B',0),'b')]
print(min(points, key=lambda y: next(b for (a,b),c in [y])))

Ya da biraz daha iyi

print(min(values, key=lambda y: next(b for ((a,b),c) in (y,))))

Bu sadece yapılabileceğini ve bir öneri olarak alınmaması gerektiğini önermek içindir.


1

Sanırım daha iyi sözdizimi x * x + y * y let x, y = point,let anahtar kelimenin daha dikkatli seçilmesi gerektiğidir.

Çift lambda en yakın versiyondur. lambda point: (lambda x, y: x * x + y * y)(*point)

Yüksek dereceli fonksiyon yardımcısı, ona uygun bir isim vermemiz durumunda faydalı olacaktır.

def destruct_tuple(f):
  return lambda args: f(*args)

destruct_tuple(lambda x, y: x * x + y * y)

0

Cuadue önerisine ve hala kavrayışta mevcut olan ambalajın açılmasıyla ilgili yorumunuza dayanarak, şunları kullanabilirsiniz numpy.argmin:

result = points[numpy.argmin(x*x + y*y for x, y in points)]

0

Diğer bir seçenek de, anahtarın ilk eleman olduğu bir demet üreten bir üreteç içine yazmaktır. Tuplelar baştan sona karşılaştırılır, böylece en küçük ilk elemente sahip tuple döndürülür. Daha sonra değeri almak için sonucu indeksleyebilirsiniz.

min((x * x + y * y, (x, y)) for x, y in points)[1]

0

İlk olarak tuple'ı açmanız gerekip gerekmediğini düşünün:

min(points, key=lambda p: sum(x**2 for x in p))

veya ambalajı açarken açık isimler vermeniz gerekip gerekmediği:

min(points, key=lambda p: abs(complex(*p))
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.