Удаление экземпляра объекта в списке python
Думаю, это должно работать, но это дает мне ошибку.
У меня есть список, содержащий объекты класса node
. У меня есть два разных списка.
- open_list
- node_list (они не одинаковы по длине, упорядочиваются)
Когда я нахожу конкретный node в open_list
, мне нужно удалить его из node_list
. Я знаю, что списки имеют адреса для объектов, хранящихся в них
поэтому, когда я пытаюсь сделать
removed = open_list.pop(min_index)
node_list.remove(removed)
он дает мне ошибку, говоря
node_list.remove(removed)
ValueError: list.remove(x): x not in list
но список просто содержит адреса, которые действуют как указатели вправо? он должен совпадать с теми же адресами. я распечатал адрес removed
и весь node_list
(только 10 элементов на данный момент не боятся)
print out: (последний элемент в node_list соответствует адресу удаленной:
removed: <__main__.node instance at 0x0124A440>
node_list: [<__main__.node instance at 0x01246E90>, <__main__.node instance at 0x01246EE0>, <__main__.node instance at 0x0124A300>, <__main__.node instance at 0x0124A328>, <__main__.node instance at 0x0124A350>, <__main__.node instance at 0x0124A378>, <__main__.node instance at 0x0124A3A0>, <__main__.node instance at 0x0124A3C8>, <__main__.node instance at 0x0124A3F0>, <__main__.node instance at 0x0124A418>, <__main__.node instance at 0x0124A440>]
Спасибо
последующий Q
поэтому я хочу проверить, существует ли node, который я хочу удалить, в списке узлов. когда я просмотрел некоторые простые функции списка на http://docs.python.org/tutorial/datastructures.html
list.index(x)
и remove.index(x)
обе дают ошибку, если элемент отсутствует в списке. это заставило мою программу перестать работать.
чтобы обойти это, могу ли я использовать этот оператор перед .remove()
: node in node_list
Я думаю, что in
проверяет, является ли элемент частью списка и возвращает bool.
просто двойная проверка
спасибо,
Ответы
Ответ 1
Это происходит потому, что то, что вы понимаете как идентификационные признаки двух экземпляров вашего класса Node
, не так, как это понимает python.
Проблема здесь. Предположим, вы спросили python 5==5
, python вернет True
. Это связано с тем, что python знает о int
s. Тем не менее, Node
- это настраиваемый пользовательский класс, поэтому вам нужно указать python, когда два объекта Node
совпадают. Поскольку у вас (возможно) нет, python по умолчанию сравнивает свои местоположения в памяти. Поскольку два отдельных экземпляра будут находиться в двух разных ячейках памяти, python вернет False
. Если вы вообще знакомы с Java, это похоже на разницу между ==
и .equals(...)
Чтобы сделать это, перейдите в свой класс Node
и определите метод __eq__(self, other)
, где other
ожидается как другой экземпляр Node
.
Например, если ваши узлы имеют атрибут с именем name
, а два узла с одинаковым именем считаются одинаковыми, то ваш __eq__
может выглядеть так:
def __eq__(self, other):
myName = self.name
hisName = other.name
if myName == hisName:
return True
else:
return False
Конечно, более элегантный способ написания этой же функции:
def __eq__(self, other):
return self.name == other.name
Когда это будет сделано, ваша ошибка должна исчезнуть
РЕДАКТИРОВАТЬ 1: в ответ на DSM комментарий
class Node: pass
a = [Node(), Node()]
b = a[:]
b.remove(a.pop(0))
Это сработает. Но при ближайшем рассмотрении становится очевидным, что [0] и b [0] фактически являются одним и тем же объектом. Это можно проверить, вызвав id(a[0])
и сравнив его с id(b[[0])
, чтобы подтвердить, что они действительно одинаковы
РЕДАКТИРОВАТЬ 2: В ответ на вопрос о продолжении OP (добавлен в исходный вопрос как редактирование)
Да, объект, не существующий в списке, вызовет ошибку, которая обычно останавливает поток программы. Это можно решить одним из двух способов:
if x in my_list:
my_list.remove(x)
ИЛИ
try:
my_list.remove(x)
except:
pass
Второй метод пытается удалить x
из my_list
, и если это приводит к ошибке, игнорирует ошибку
Ответ 2
Если я правильно прочитал вопрос, стандартное значение python для сравнения мест памяти - это поведение, которое он ищет, но не получает. Здесь рабочий пример, в котором определяется пользовательский класс Node
, показывает, что нет необходимости в __eq__(self, other)
.
class Node(object):
pass
open_node_list = []
node_list = []
for i in range(10):
a_node = Node()
open_node_list.append(a_node)
node_list.append(a_node)
removed = open_node_list.pop()
node_list.remove(removed)
Я не могу быть уверен, потому что вы не указали, где были определены ваши open_node_list
и node_list
, но я подозреваю, что сами списки ссылаются на один и тот же объект списка. В этом случае выскакивание из open_node_list
также появляется из node_list
, поэтому node больше не будет существовать при вызове remove
. Вот пример, в котором node_list
и open_node_list
действительно являются одним и тем же списком, и поэтому изменения одного влияют на другое:
class Node(object):
pass
open_node_list = []
node_list = open_node_list # <-- This is a reference, not a copy.
open_node_list.append(Node())
print(node_list)
Один из способов копирования списка:
node_list = open_node_list[:]
Ответ 3
В ответ на ваш запрос yes in
проверит наличие членства в списке, поэтому:
if removed in node_list: node_list.remove(removed)
не даст вам ошибку. В качестве альтернативы вы можете уловить ошибку:
try:
node_list.remove(removed)
except ValueError:
pass