Bagaimana tugas bekerja dengan potongan daftar Python?

100

Doc Python mengatakan bahwa mengiris daftar mengembalikan daftar baru.
Sekarang jika daftar "baru" dikembalikan, saya memiliki pertanyaan berikut terkait dengan "Penugasan ke irisan"

a = [1, 2, 3]
a[0:2] = [4, 5]
print a

Sekarang hasilnya adalah:

[4, 5, 3] 
  1. Bagaimana bisa sesuatu yang mengembalikan sesuatu muncul di sisi kiri ekspresi?
  2. Ya, saya membaca dokumen dan dikatakan mungkin, sekarang karena pemotongan daftar menghasilkan daftar "baru", mengapa daftar asli diubah? Saya tidak dapat memahami mekanisme di baliknya.
Kartik Anand
sumber
@Mark Longair maaf saya pikir hanya kode yang seharusnya diformat bukan output
Kartik Anand
2
Lihat: 6.2 Pernyataan Penugasan
Josh Lee
7
Apakah Anda mengerti apa yang a[0] = 4akan dilakukan?
Josh Lee
1
Penugasan @KartikAnand Slice adalah skenario khusus di mana daftar baru tidak dibuat. Tidak masuk akal untuk membuat objek tanpa nama yang mengikat di sisi kiri =, jadi alih-alih membuang ini sebagai sintaks yang tidak valid, python mengubahnya menjadi sesuatu yang lebih seperti yang Anda harapkan. Karena python tidak memiliki referensi, itu tidak akan berfungsi jika hasil potongan mengubah daftar aslinya. Anda mendapatkan salinannya. Jika Anda memberikan info lebih lanjut tentang aplikasi Anda, kami mungkin dapat membantu Anda melakukan berbagai hal dengan cara 'pythonic' dengan lebih baik. :)
Casey Kuball
1
@Darthfett Saya tidak sedang mengerjakan aplikasi apa pun sekarang, melainkan saya belajar sendiri python sebelum saya mulai mengotori tangan saya :)
Kartik Anand

Jawaban:

114

Anda bingung dengan dua operasi berbeda yang menggunakan sintaks yang sangat mirip:

1) mengiris:

b = a[0:2]

Ini membuat salinan dari potongan adan menetapkannya keb .

2) tugas potong:

a[0:2] = b

Ini menggantikan irisan adengan kontenb .

Meskipun sintaksnya serupa (saya membayangkan desainnya!), Ini adalah dua operasi yang berbeda.

NPE
sumber
4
Itulah yang saya ragu, dalam kasus kedua, mengapa potongan a, daftar baru ??
Kartik Anand
11
@Kikik Karena bukan. Bukan itu yang ditentukan oleh bahasanya.
Marcin
Untuk lebih jelasnya, "mengambil sepotong" benar-benar berarti "membuat salinan dari sepotong" yang merupakan asal dari sebagian kebingungan.
Markus Ransom
2
@Kartikand: Pada dasarnya ya. Penerjemah tahu yang mana, dan menanganinya dengan tepat.
NPE
1
@Dubslow: Anda dapat melakukannya dengan menggunakan modul itertools . Untuk kasus Anda menggunakan fungsi islice , dengan start=1, stop=None. Ini akan menghindari penyalinan dan menggunakan evaluasi malas (dalam kasus Anda akses malas atau daftar asli).
Spiros
66

Saat Anda menentukan adi sisi kiri =operator, Anda menggunakan tugas normal Python , yang mengubah nama adalam konteks saat ini untuk menunjuk ke nilai baru. Ini tidak mengubah nilai sebelumnya yang aditunjuk.

Dengan menentukan a[0:2]di sisi kiri =operator, Anda memberi tahu Python bahwa Anda ingin menggunakan Slice Assignment . Slice Assignment adalah sintaks khusus untuk daftar, di mana Anda dapat menyisipkan, menghapus, atau mengganti konten dari daftar:

Penyisipan :

>>> a = [1, 2, 3]
>>> a[0:0] = [-3, -2, -1, 0]
>>> a
[-3, -2, -1, 0, 1, 2, 3]

Penghapusan :

>>> a
[-3, -2, -1, 0, 1, 2, 3]
>>> a[2:4] = []
>>> a
[-3, -2, 1, 2, 3]

Penggantian :

>>> a
[-3, -2, 1, 2, 3]
>>> a[:] = [1, 2, 3]
>>> a
[1, 2, 3]

catatan:

Panjang irisan mungkin berbeda dari panjang urutan yang ditetapkan, sehingga mengubah panjang urutan target, jika urutan target memungkinkan. - sumber

Slice Assignment menyediakan fungsi yang mirip dengan Tuple Unpacking . Misalnya, a[0:1] = [4, 5]setara dengan:

# Tuple Unpacking
a[0], a[1] = [4, 5]

Dengan Tuple Unpacking, Anda dapat mengubah daftar non-berurutan:

>>> a
[4, 5, 3]
>>> a[-1], a[0] = [7, 3]
>>> a
[3, 5, 7]

Namun, pembongkaran tuple terbatas pada penggantian, karena Anda tidak dapat memasukkan atau menghapus elemen.

Sebelum dan sesudah semua operasi ini, aadalah daftar yang persis sama. Python hanya menyediakan gula sintaksis yang bagus untuk mengubah daftar di tempat.

Casey Kuball
sumber
6
Serupa tetapi tidak identik, karena Anda dapat memiliki jumlah elemen yang tidak sama di kiri dan kanan.
Markus Ransom
@MarkRansom Itu poin yang sangat bagus, saya telah menambahkan lebih banyak info untuk membuatnya jelas.
Casey Kuball
2
Apakah a[:] = some_listsetara dengan a = some_list[:]atau a = some_list?
jadkik94
2
@ jaddahh. a[:] = some_listmenetapkan setiap elemen amenjadi milik some_list. Melakukan salah satu dari yang Anda sebutkan akan mengubah apa yang aada. Sebagai contoh: a = [1, 2, 3] b = a a[:] = [4, 5, 6] a is b. Baris terakhir akan menjadi False jika itu mengubah anilainya, daripada memutasinya.
Casey Kuball
@Darthfett Menarik, saya menemukan sebaliknya :) Terima kasih.
jadkik94
25

Saya menemukan pertanyaan yang sama sebelumnya dan ini terkait dengan spesifikasi bahasa. Menurut pernyataan penugasan ,

  1. Jika sisi kiri tugas adalah berlangganan, Python akan memanggil __setitem__objek itu. a[i] = xsetara dengan a.__setitem__(i, x).

  2. Jika sisi kiri tugas adalah slice, Python juga akan memanggil __setitem__, tetapi dengan argumen yang berbeda: a[1:4]=[1,2,3]setara dengan a.__setitem__(slice(1,4,None), [1,2,3])

Itulah mengapa potongan daftar di sisi kiri '=' berperilaku berbeda.

Stan
sumber
4

Dengan mengiris di sisi kiri operasi tugas, Anda menentukan item mana yang akan ditugaskan.

fraxel
sumber