Bagaimana cara mengubah entri daftar selama untuk loop?

178

Sekarang saya tahu bahwa tidak aman untuk mengubah daftar selama perulangan berulang. Namun, anggaplah saya memiliki daftar string, dan saya ingin menghapus string sendiri. Apakah penggantian nilai yang dapat diubah dianggap sebagai modifikasi?

Alex
sumber
20
Sebuah string tidak menghasilkan nilai yang bisa berubah.
user470379
4
@ user470379: Apakah elemen dari daftar bisa berubah tidak relevan dengan apakah aman atau tidak untuk mengubah daftar mereka saat pengulangan meskipun itu.
martineau

Jawaban:

144

Itu dianggap bentuk yang buruk. Gunakan pemahaman daftar sebagai gantinya, dengan tugas irisan jika Anda perlu mempertahankan referensi yang ada ke daftar.

a = [1, 3, 5]
b = a
a[:] = [x + 2 for x in a]
print(b)
Ignacio Vazquez-Abrams
sumber
10
Tugas slice cerdas dan menghindari memodifikasi asli selama loop, tetapi membutuhkan pembuatan daftar sementara panjang aslinya.
martineau
11
mengapa kita menetapkan b = a?
Vigrond
9
@Vigrond: Jadi, ketika print bpernyataan dijalankan, Anda dapat mengetahui apakah atelah dimodifikasi di tempat daripada diganti. Kemungkinan lain adalah print b is amelihat apakah keduanya masih merujuk pada objek yang sama.
martineau
12
mengapa a [:] = dan bukan hanya a =?
kdubs
10
@kdubs: "... dengan tugas slice jika Anda perlu mempertahankan referensi yang ada ke daftar."
Ignacio Vazquez-Abrams
163

Karena loop di bawah ini hanya memodifikasi elemen yang sudah dilihat, itu akan dianggap dapat diterima:

a = ['a',' b', 'c ', ' d ']

for i, s in enumerate(a):
    a[i] = s.strip()

print(a) # -> ['a', 'b', 'c', 'd']

Yang berbeda dari:

a[:] = [s.strip() for s in a]

dalam hal itu tidak memerlukan pembuatan daftar sementara dan penugasan untuk menggantikan yang asli, meskipun itu membutuhkan lebih banyak operasi pengindeksan.

Perhatian: Meskipun Anda dapat memodifikasi entri dengan cara ini, Anda tidak dapat mengubah jumlah item listtanpa mempertaruhkan peluang menghadapi masalah.

Inilah contoh yang saya maksud — menghapus entri mengacaukan pengindeksan sejak saat itu:

b = ['a', ' b', 'c ', ' d ']

for i, s in enumerate(b):
    if s.strip() != b[i]:  # leading or trailing whitespace?
        del b[i]

print(b)  # -> ['a', 'c ']  # WRONG!

(Hasilnya salah karena tidak menghapus semua item yang seharusnya.)

Memperbarui

Karena ini adalah jawaban yang cukup populer, berikut ini cara menghapus entri "di tempat" secara efektif (meskipun itu bukan pertanyaan):

b = ['a',' b', 'c ', ' d ']

b[:] = [entry for entry in b if entry.strip() == entry]

print(b)  # -> ['a']  # CORRECT
martineau
sumber
3
Mengapa Python hanya membuat salinan dari elemen individual dalam sintaks for i in a? Ini sangat berlawanan dengan intuisi, tampaknya berbeda dari bahasa lain dan telah mengakibatkan kesalahan pada kode saya sehingga saya harus melakukan debug untuk jangka waktu yang lama. Tutorial Python bahkan tidak menyebutkannya. Meskipun harus ada alasan untuk itu?
xji
1
@ JIXiang: Itu tidak membuat salinan. Itu hanya memberikan nama variabel loop ke elemen yang berurutan atau nilai dari hal yang iterated-upon.
martineau
1
Eww, mengapa menggunakan dua nama ( a[i]dan s) untuk objek yang sama di baris yang sama ketika Anda tidak perlu? Saya lebih suka melakukannya a[i] = a[i].strip().
Navin
3
@Navin: Karena a[i] = s.strip()hanya satu operasi pengindeksan.
martineau
1
@martineau enumerate(b)melakukan operasi pengindeksan pada setiap iterasi dan Anda melakukannya dengan yang lain a[i] =. AFAIK tidak mungkin untuk mengimplementasikan loop ini dalam Python dengan hanya 1 operasi pengindeksan per loop iterasi :(
Navin
18

Satu lagi untuk varian loop, terlihat lebih bersih bagi saya daripada satu dengan enumerate ():

for idx in range(len(list)):
    list[idx]=... # set a new value
    # some other code which doesn't let you use a list comprehension
Eugene Shatsky
sumber
19
Banyak yang menganggap menggunakan sesuatu seperti range(len(list))di Python sebagai bau kode.
martineau
2
@Reishin: Karena enumerategenerator itu tidak membuat daftar. Dari tupel, itu membuat mereka satu per satu saat iterates melalui daftar. Satu-satunya cara untuk mengetahui mana yang lebih lambat adalah dengan timeit.
martineau
3
Kode @martineau mungkin tidak terlalu bagus, tetapi menurutnya timeit enumeratelebih lambat
Reishin
2
@Reishin: Kode pembandingan Anda tidak sepenuhnya valid karena tidak memperhitungkan kebutuhan untuk mengambil nilai dalam daftar pada indeks yang diberikan - yang juga tidak ditampilkan dalam jawaban ini.
martineau
4
@Reishin: Perbandingan Anda tidak valid tepatnya karena alasan itu. Ini mengukur overhead looping secara terpisah. Agar konklusif waktu yang diperlukan seluruh loop untuk mengeksekusi harus diukur karena kemungkinan bahwa setiap perbedaan overhead dapat dikurangi dengan manfaat yang diberikan kepada kode di dalam loop dari looping dengan cara tertentu - jika tidak Anda tidak membandingkan apel dengan apel.
martineau
11

Memodifikasi setiap elemen saat iterasi daftar baik-baik saja, selama Anda tidak mengubah menambah / menghapus elemen ke daftar.

Anda dapat menggunakan pemahaman daftar:

l = ['a', ' list', 'of ', ' string ']
l = [item.strip() for item in l]

atau lakukan saja C-stylefor loop:

for index, item in enumerate(l):
    l[index] = item.strip()
cizixs
sumber
4

Tidak, Anda tidak akan mengubah "konten" daftar, jika Anda dapat mengubah string dengan cara itu. Tetapi dalam Python mereka tidak bisa berubah. Operasi string apa pun mengembalikan string baru.

Jika Anda memiliki daftar objek yang Anda tahu bisa berubah, Anda bisa melakukan ini selama Anda tidak mengubah konten sebenarnya dari daftar.

Dengan demikian Anda perlu melakukan semacam peta. Jika Anda menggunakan ekspresi generator itu [operasi] akan dilakukan saat Anda mengulanginya dan Anda akan menghemat memori.

Skurmedel
sumber
4

Anda dapat melakukan sesuatu seperti ini:

a = [1,2,3,4,5]
b = [i**2 for i in a]

Ini disebut pemahaman daftar, untuk membuatnya lebih mudah bagi Anda untuk masuk ke dalam daftar.

Nenoj
sumber
3

Jawaban yang diberikan oleh Jemshit Iskenderov dan Ignacio Vazquez-Abrams sangat bagus. Lebih lanjut dapat diilustrasikan dengan contoh ini: bayangkan itu

a) Daftar dengan dua vektor diberikan kepada Anda;

b) Anda ingin melintasi daftar dan membalikkan urutan masing-masing array

Katakanlah Anda punya

v = np.array([1, 2,3,4])
b = np.array([3,4,6])

for i in [v, b]:
    i = i[::-1]   # this command does not reverse the string

print([v,b])

Kamu akan mendapatkan

[array([1, 2, 3, 4]), array([3, 4, 6])]

Di sisi lain, jika Anda melakukannya

v = np.array([1, 2,3,4])
b = np.array([3,4,6])

for i in [v, b]:
   i[:] = i[::-1]   # this command reverses the string

print([v,b])

Hasilnya adalah

[array([4, 3, 2, 1]), array([6, 4, 3])]
Rafael Monteiro
sumber
1

Tidak jelas dari pertanyaan Anda apa kriteria untuk memutuskan string apa yang harus dihapus, tetapi jika Anda memiliki atau dapat membuat daftar string yang ingin Anda hapus, Anda dapat melakukan hal berikut:

my_strings = ['a','b','c','d','e']
undesirable_strings = ['b','d']
for undesirable_string in undesirable_strings:
    for i in range(my_strings.count(undesirable_string)):
        my_strings.remove(undesirable_string)

yang mengubah my_strings menjadi ['a', 'c', 'e']

Jorge
sumber
0

Singkatnya, untuk melakukan modifikasi pada daftar sambil mengulangi daftar yang sama.

list[:] = ["Modify the list" for each_element in list "Condition Check"]

contoh:

list[:] = [list.remove(each_element) for each_element in list if each_element in ["data1", "data2"]]
keseimbangan air liur
sumber