Apa padanan python3 yang baik untuk pembongkaran tupel otomatis di lambda?

97

Perhatikan kode python2 berikut

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

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

Ini tidak didukung di python3 dan saya harus melakukan hal berikut:

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

Ini sangat jelek. Jika lambda adalah sebuah fungsi, saya bisa melakukannya

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

Menghapus fitur ini di python3 memaksa kode untuk melakukan cara yang buruk (dengan indeks ajaib) atau membuat fungsi yang tidak perlu (Bagian yang paling mengganggu adalah memikirkan nama yang baik untuk fungsi yang tidak perlu ini)

Saya pikir fitur tersebut harus ditambahkan kembali setidaknya ke lambda saja. Apakah ada alternatif yang bagus?


Pembaruan: Saya menggunakan pembantu berikut untuk memperluas ide dalam jawaban

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

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

Pembaruan2: Versi yang lebih bersih untukstar

import functools

def star(f):
    @functools.wraps(f):
    def f_inner(args):
        return f(*args)
    return f_inner
balki
sumber
4
Mungkin lebih mungkin untuk lambdadihapus dari bahasa seluruhnya daripada membalikkan perubahan yang membuatnya lebih sulit untuk digunakan, tetapi Anda dapat mencoba memposting di python-ideas jika Anda ingin mengungkapkan keinginan untuk melihat fitur ditambahkan kembali.
Wooble
3
Saya juga tidak mengerti, tetapi sepertinya BDFL menentang lambdadengan semangat yang sama seperti yang dia lawan map, reducedan filter.
Cuadue
3
lambdadijadwalkan untuk dihapus di py3k karena pada dasarnya ini adalah kerusakan pada bahasa. Tapi tidak ada yang bisa menyetujui alternatif yang tepat untuk mendefinisikan fungsi anonim, jadi akhirnya Guido angkat tangan karena kalah dan hanya itu.
roippi
5
fungsi anonim harus dimiliki dalam bahasa yang sesuai, dan saya sangat menyukai lambda. Saya harus membaca mengapa debat seperti itu. (Juga, meskipun mapdan filterpaling baik diganti dengan pemahaman, saya suka reduce)
njzk2
13
Satu hal yang saya tidak suka tentang Python 3 ...
timgeb

Jawaban:

34

Tidak, tidak ada cara lain. Anda menutupi semuanya. Cara yang harus dilakukan adalah mengangkat masalah ini di milis Ide Python , tetapi bersiaplah untuk banyak berdebat di sana untuk mendapatkan daya tarik.

Sebenarnya, hanya untuk tidak mengatakan "tidak ada jalan keluar", cara ketiga adalah dengan menerapkan satu tingkat panggilan lambda lagi hanya untuk membuka parameter - tetapi itu akan menjadi lebih tidak efisien dan lebih sulit untuk dibaca daripada dua saran Anda:

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

perbarui Python 3.8

Saat ini, Python 3.8 alpha1 tersedia, dan ekspresi tugas PEP 572- diimplementasikan.

Jadi, jika seseorang menggunakan trik untuk mengeksekusi beberapa ekspresi di dalam lambda - saya biasanya melakukannya dengan membuat tupel dan hanya mengembalikan komponen terakhirnya, itu mungkin untuk dilakukan:

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

Perlu diingat bahwa kode semacam ini jarang lebih mudah dibaca atau praktis daripada memiliki fungsi penuh. Namun, ada kemungkinan penggunaan - jika ada berbagai satu-liner yang akan beroperasi pada ini point, mungkin ada baiknya untuk memiliki nametuple, dan menggunakan ekspresi assignment untuk secara efektif "mentransmisikan" urutan yang masuk ke namaiuple:

>>> from collections import namedtuple
>>> point = namedtuple("point", "x y")
>>> b = lambda s: (p:=point(*s), p.x ** 2 + p.y ** 2)[-1]
jsbueno
sumber
3
Dan tentu saja, saya lupa menyebutkan bahwa cara "satu dan jelas" untuk melakukan ini adalah dengan menggunakan fungsi tiga baris defyang disebutkan dalam pertanyaan di bawah nme some_name_to_think-of.
jsbueno
2
Jawaban ini masih menarik perhatian beberapa pengunjung - dengan implementasi PEP 572, seharusnya ada cara untuk membuat variabel di dalam ekspresi lambda, seperti di:key = lambda p: (x:=p[0], y:=p[1], x ** 2 + y ** 2)[-1]
jsbueno
1
ekspresi tugas !! Saya (dengan senang hati) terkejut mereka berhasil masuk
StephenBoesch
24

Menurut http://www.python.org/dev/peps/pep-3113/ tuple unpacking sudah tidak ada, dan 2to3akan menerjemahkannya seperti ini:

Karena parameter tupel digunakan oleh lambda karena batasan ekspresi tunggal, mereka juga harus didukung. Ini dilakukan dengan memiliki argumen urutan yang diharapkan terikat ke satu parameter dan kemudian mengindeks parameter itu:

lambda (x, y): x + y

akan diterjemahkan menjadi:

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

Yang sangat mirip dengan penerapan Anda.

njzk2.dll
sumber
3
Baik bahwa itu tidak dihapus untuk loop atau pemahaman
balki
12

Saya tidak tahu alternatif umum yang baik untuk perilaku membongkar argumen Python 2. Berikut beberapa saran yang mungkin berguna dalam beberapa kasus:

  • jika Anda tidak dapat memikirkan sebuah nama; gunakan nama parameter kata kunci:

    def key(p): # more specific name would be better
        x, y = p
        return x**2 + y**3
    
    result = min(points, key=key)
    
  • Anda dapat melihat apakah a namedtuplemembuat kode Anda lebih mudah dibaca jika daftar digunakan di banyak tempat:

    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)
    
jfs
sumber
Di antara semua jawaban untuk pertanyaan ini sejauh ini, menggunakan nametuple adalah tindakan terbaik di sini. Tidak hanya Anda dapat menyimpan lambda Anda tetapi juga saya menemukan bahwa versi bernamatuple lebih mudah dibaca sebagai perbedaan antara lambda (x, y):dan lambda x, y:tidak jelas pada pandangan pertama.
Burak Arslan
5

Sementara argumen penghancur telah dihapus dengan Python3, itu tidak dihapus dari pemahaman. Dimungkinkan untuk menyalahgunakannya untuk mendapatkan perilaku yang serupa dengan Python 3. Intinya, kami memanfaatkan fakta bahwa rutinitas bersama memungkinkan kita untuk mengubah fungsi ke luar, dan hasil bukanlah pernyataan, dan karenanya diizinkan dalam lambda.

Sebagai contoh:

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

Dibandingkan dengan jawaban yang diterima untuk menggunakan pembungkus, solusi ini dapat sepenuhnya merusak argumen sementara pembungkus hanya merusak tingkat pertama. Itu adalah,

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

Dibandingkan dengan

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

Alternatifnya juga bisa dilakukan

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

Atau sedikit lebih baik

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

Ini hanya untuk memberi kesan bahwa hal itu dapat dilakukan, dan tidak boleh dianggap sebagai rekomendasi.

rahul
sumber
1

Menurut saya sintaks yang lebih baik adalah x * x + y * y let x, y = point, letkata kunci harus dipilih dengan lebih cermat.

Lambda ganda adalah versi terdekat. lambda point: (lambda x, y: x * x + y * y)(*point)

Fungsi pembantu tingkat tinggi akan berguna jika kita memberinya nama yang tepat.

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

destruct_tuple(lambda x, y: x * x + y * y)
anthony.hl
sumber
0

Berdasarkan saran Cuadue dan komentar Anda tentang membongkar masih ada dalam pemahaman, Anda dapat menggunakan, menggunakan numpy.argmin:

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

Pilihan lainnya adalah menuliskannya ke generator yang menghasilkan tupel di mana kuncinya adalah elemen pertama. Tupel dibandingkan mulai dari awal hingga akhir sehingga tupel dengan elemen pertama terkecil dikembalikan. Anda kemudian dapat mengindeks hasil untuk mendapatkan nilainya.

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

Pertimbangkan apakah Anda perlu membongkar tupel terlebih dahulu:

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

atau apakah Anda perlu memberikan nama eksplisit saat membongkar:

min(points, key=lambda p: abs(complex(*p))
chepner
sumber