El akarom távolítani az összes páros számot a listából. De valami megzavart... Ez a kód.
lst = [4,4,5,5]
for i in lst:
if i % 2 == 0:
print i
lst.remove(i)
print lst
Kinyomtatja [4, 5, 5] Miért nem [5, 5]?
El akarom távolítani az összes páros számot a listából. De valami megzavart... Ez a kód.
lst = [4,4,5,5]
for i in lst:
if i % 2 == 0:
print i
lst.remove(i)
print lst
Kinyomtatja [4, 5, 5] Miért nem [5, 5]?
Ilyennek kell lennie
for i in lst[:]:
if i % 2 == 0:
print i
lst.remove(i)
print lst
Probléma:
Módosítja a listát, miközben ismételget rajta. Emiatt az iteráció leáll, mielőtt befejeződhetne
Megoldás:
Ismételheti a lista másolatát
Használhatja a list comprehension
:
lst=[i for i in lst if i%2 != 0]
A list.remove
használatával módosítja a listát az iteráció során. Ez megszakítja az iterációt, és váratlan eredményeket kap.
Az egyik megoldás egy új lista létrehozása a filter
vagy a listaértelmezés használatával:
>>> filter(lambda i: i % 2 != 0, lst)
[5, 5]
>>> [i for i in lst if i % 2 != 0]
[5, 5]
Ha szükséges, bármelyik kifejezést hozzárendelheti a lst
-hez, de ezekkel a metódusokkal nem kerülheti el az új listaobjektum létrehozását.
Más válaszok már említették, hogy módosítja a listát, miközben iterál rajta, és jobb módszereket kínáltak erre. Én személy szerint jobban szeretem a listaértési módszert:
odd_numbers = [item for item in numbers if item % 2 != 0]
A megadott esetedben egy nagyon kis lista esetén én mindenképpen ezt választanám.
Ez azonban egy új listát hoz létre, ami problémát jelenthet, ha nagyon nagy listája van. Egész számok esetében a nagy valószínűleg legalább milliókat jelent, de hogy pontos legyek, bármilyen nagynak kell lennie ahhoz, hogy memóriahasználati problémákat okozzon. Ebben az esetben itt van néhány módszer.
Az egyik mód hasonló a kérdésben szereplő kód szándékához. Iterálhatja a listát, miközben menet közben eltávolítja a páros számokat. Azonban, hogy elkerülje azokat a problémákat, amelyeket az iterált lista módosítása okozhat, ismételje meg visszafelé. Vannak módok a továbblépésre, de ez egyszerűbb.
Íme az egyik módja a while
ciklus használatának:
# A one hundred million item list that we don't want to copy
# even just the odd numbers from to put into a new list.
numbers = range(100000000) # list(range(100000000)) in Python 3
index = len(numbers) - 1 # Start on the index of the last item
while index >= 0:
if numbers[index] % 2 == 0:
numbers.pop(index)
index -= 1
Íme egy másik módja a for
ciklus használatának:
# A one hundred million item list that we don't want to copy
# even just the odd numbers from to put into a new list.
numbers = range(100000000) # list(range(100000000)) in Python 3
for index in xrange(len(numbers) - 1, -1, -1): # range(...) in Python 3
if numbers[index] % 2 == 0:
numbers.pop(index)
Figyeljük meg mind a while
ciklusos, mind a for
ciklusos verzióban, én a numbers.pop(index)
-t használtam, nem a numbers.remove(numbers[index])
-t. Először is, a .pop()
sokkal hatékonyabb, mert ez biztosítja az indexet, míg a .remove()
-nek a listában kell keresnie az érték első előfordulását. Másodszor, vedd észre, hogy azt mondtam: "az érték első előfordulása". Ez azt jelenti, hogy kivéve, ha minden elem egyedi, a .remove()
használata egy másik elemet távolít el, mint amilyenben a hurok jelenleg van, ami végül az aktuális elemet hagyja a listában.
Még egy megoldást szeretnék megemlíteni olyan helyzetekre, amikor meg kell őriznie az eredeti listát, de nem szeretne túl sok memóriát használni a páratlan számok másolatának tárolására. Ha csak egyszer szeretné átmásolni a páratlan számokat (vagy annyira ódzkodik a memóriahasználattól, hogy inkább újraszámolja a dolgokat, amikor szükséges), használhat generátort. Ezzel lehetővé tenné, hogy a listában szereplő páratlan számok felett ismételjen anélkül, hogy bármilyen további memóriára lenne szüksége, eltekintve a generátormechanizmus által felhasznált jelentéktelen mennyiségtől.
A generátor kifejezés pontosan úgy van definiálva, mint egy listaértelmezés, azzal a különbséggel, hogy szögletes zárójelek helyett zárójelben van:
odd_numbers = (item for item in numbers if item % 2 != 0)
Ne feledje, hogy a generátor kifejezés az eredeti listán ismétlődik, így az eredeti lista köztes iterációjának módosítása ugyanazokat a problémákat okozza, mint a lista módosítása, miközben egy for
ciklusban iterál rajta. Valójában maga a generátor kifejezés egy for
ciklust használ.
Félretéve, a generátor kifejezéseket nem szabad csak nagyon nagy listákra helyezni; Mindig használom őket, amikor nem kell egy teljes listát kiszámolnom.
A „legjobb” módszer attól függ, hogy pontosan mit csinálsz, de ennek sok helyzetre ki kell terjednie.
Tételezzük fel, hogy a listák "kicsiek" vagy "nagyok":
Ha a listája kicsi, használja a lista megértését (vagy akár a generátor kifejezést, ha tudja). Ha nagy, olvass tovább.
Ha nincs szüksége az eredeti listára, használja a while
ciklus vagy a for
ciklus metódusait a páros számok teljes eltávolításához (bár .pop()
, nem .remove()
). Ha szüksége van az eredeti listára, olvassa el.
Ha csak egyszer iterál a páratlan számok felett, használja a generátor kifejezést. Ha többször iterálja őket, de hajlandó megismételni a számítást a memória megtakarítása érdekében, használja a generátor kifejezést.
Ha túl sokszor iterálja a páratlan számokat ahhoz, hogy minden alkalommal újraszámítsa őket, vagy véletlenszerű hozzáférésre van szüksége, akkor a listaértelmezés segítségével készítsen új listát, amelyben csak a páratlan számok szerepelnek. Sok memóriát fog igénybe venni, de ezek a szünetek.
Általános elv, hogy ne módosítsa a gyűjteményt, miközben iterál rajta. Ez egyes elemek kihagyásához és bizonyos esetekben indexhibához vezet.
Ahelyett, hogy eltávolítana elemeket a listáról, egyszerűbb lenne, ha létrehozna egy másik, azonos nevű hivatkozást. Kisebb időbonyolítással is rendelkezik.
lst = filter(lambda i: i % 2 !=0, lst)