Не могу понять, почему некорректно работает функция pop
Написал функцию, которая должна убирать из списка единично встречающиеся цифры.
def checkio(data: list) -> list:
output = data
for i in range(len(data)):
if data.count(data[i]) == 1:
output.pop(output.index(data[i]))
return output
При запуске с такими аргументами checkio([1,2,3,1,3]) выдает ошибку
IndexError: list index out of range
Методом проб и ошибок пришел к выводу, что если во второй строке добавить функцию list(), то все начинает работать корректно.
def checkio(data: list) -> list:
output = list(data)
for i in range(len(data)):
if data.count(data[i]) == 1:
output.pop(output.index(data[i]))
return output
Не могу понять в чем проблем. Ведь и data и output имеют вид list без каких-либо дополнительных уточнений. Почему же требует указывать, что output = list(data), а просто output = data не работает?
Ответы (2 шт):
Потому что output = data - это не копирование списка. Обе переменных будут указывать на одни и те же данные, и при модифицировании одного списка "плывёт" и другой. Т.к. после output.pop() элемент исчезает и из списка data.
Для копирования содержимого списка в другой список используется запись вида output = data[:] или output = data.copy().
Да и вообще:
def checkio(data: list) -> list:
return [el for el in data if data.count(el) > 1]
Присвоение в Python не приводит к копированию объекта:
$ python
@>>> a = list(range(5))
@>>> a
[0, 1, 2, 3, 4]
@>>> b = a
@>>> a is b
True
@>>> b.pop(2)
2
@>>> b
[0, 1, 3, 4]
@>>> a
[0, 1, 3, 4]
Видно что a и b ссылаются на один и тот же объект. Если вызвать list то будет создана копия:
$ python
@>>> a = list(range(5))
@>>> a
[0, 1, 2, 3, 4]
@>>> b = list(a)
@>>> a is b
False
@>>> b.pop(2)
2
@>>> b
[0, 1, 3, 4]
@>>> a
[0, 1, 2, 3, 4]
Возвращаясь к вашей задаче. Если вы хотите создать модифицированную копию списка вам нужен list comprehension. Чтобы не пересчитывать элементы списка много раз, используйте collections.Counter. Он считает один раз все вхождения всех элементов и формирует словарь счётчиков:
import collections
def checkio(data: list) -> list:
c = collections.Counter(data)
return [v for v in data if c[v] > 1]