Ця відповідь була спочатку написана у відповідь на питання, яке з тих пір було позначене як дублікат:
Видалення координат зі списку на python
У коді є дві проблеми:
1) Використовуючи delete (), ви намагаєтесь видалити цілі числа, тоді як вам потрібно видалити кортеж.
2) Цикл for пропустить елементи у вашому списку.
Давайте розберемося, що станеться, коли ми виконаємо ваш код:
>>> L1 = [(1,2), (5,6), (-1,-2), (1,-2)]
>>> for (a,b) in L1:
... if a < 0 or b < 0:
... L1.remove(a,b)
...
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
TypeError: remove() takes exactly one argument (2 given)
Перша проблема полягає в тому, що ви передаєте і "a", і "b", щоб видалити (), але delete () приймає лише один аргумент. Тож як ми можемо змусити delete () працювати належним чином зі своїм списком? Нам потрібно розібратися, що таке кожен ваш список. У цьому випадку кожен з них є кортежем. Щоб побачити це, давайте отримаємо доступ до одного елемента списку (індексація починається з 0):
>>> L1[1]
(5, 6)
>>> type(L1[1])
<type 'tuple'>
Ага! Кожен елемент L1 насправді є кортежем. Отже, це нам потрібно пройти, щоб видалити (). Кортежі в python дуже прості, вони просто зроблені шляхом додавання значень у круглі дужки. "a, b" - не кортеж, але "(a, b)" - кортеж. Тож ми змінюємо ваш код і запускаємо його знову:
# The remove line now includes an extra "()" to make a tuple out of "a,b"
L1.remove((a,b))
Цей код працює без помилок, але давайте подивимось на список, який він виводить:
L1 is now: [(1, 2), (5, 6), (1, -2)]
Чому (1, -2) досі у вашому списку? Виявляється, зміна списку під час використання циклу для повторення над ним - дуже погана ідея без особливого догляду. Причиною того, що (1, -2) залишається у списку, є те, що розташування кожного елемента в списку змінювались між ітераціями циклу for. Давайте подивимось, що станеться, якщо ми подамо вищевказаний код більш довгий список:
L1 = [(1,2),(5,6),(-1,-2),(1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)]
### Outputs:
L1 is now: [(1, 2), (5, 6), (1, -2), (3, 4), (5, 7), (2, 1), (5, -1), (0, 6)]
Як ви можете зробити висновок з цього результату, щоразу, коли умовне твердження оцінюється як істинне і елемент списку видаляється, наступна ітерація циклу буде пропускати оцінку наступного елемента в списку, оскільки його значення тепер знаходяться в різних індексах.
Найінтуїтивніше рішення - скопіювати список, потім повторити його над початковим списком і лише змінити копію. Ви можете спробувати зробити так:
L2 = L1
for (a,b) in L1:
if a < 0 or b < 0 :
L2.remove((a,b))
# Now, remove the original copy of L1 and replace with L2
print L2 is L1
del L1
L1 = L2; del L2
print ("L1 is now: ", L1)
Однак вихід буде ідентичним раніше:
'L1 is now: ', [(1, 2), (5, 6), (1, -2), (3, 4), (5, 7), (2, 1), (5, -1), (0, 6)]
Це тому, що коли ми створили L2, python насправді не створив нового об’єкта. Натомість він просто посилається на L2 на той самий об’єкт, що і L1. Ми можемо перевірити це за допомогою "є", що відрізняється від просто "рівного" (==).
>>> L2=L1
>>> L1 is L2
True
Ми можемо зробити справжню копію за допомогою copy.copy (). Тоді все працює як очікувалося:
import copy
L1 = [(1,2), (5,6),(-1,-2), (1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)]
L2 = copy.copy(L1)
for (a,b) in L1:
if a < 0 or b < 0 :
L2.remove((a,b))
# Now, remove the original copy of L1 and replace with L2
del L1
L1 = L2; del L2
>>> L1 is now: [(1, 2), (5, 6), (3, 4), (5, 7), (2, 1), (0, 6)]
Нарешті, є одне більш чисте рішення, ніж робити абсолютно нову копію L1. Зворотна () функція:
L1 = [(1,2), (5,6),(-1,-2), (1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)]
for (a,b) in reversed(L1):
if a < 0 or b < 0 :
L1.remove((a,b))
print ("L1 is now: ", L1)
>>> L1 is now: [(1, 2), (5, 6), (3, 4), (5, 7), (2, 1), (0, 6)]
На жаль, я не можу адекватно описати, як працює реверс (). Він повертає об'єкт 'listreverseiterator', коли список передається йому. У практичних цілях ви можете вважати це створенням зворотної копії його аргументу. Це рішення, яке я рекомендую.