Каков самый быстрый алгоритм удаления дубликатов из списка в Python, чтобы все элементы были уникальными * при сохранении порядка *?

Например:

>>> x = [1, 1, 2, 'a', 'a', 3]
>>> unique(x)
[1, 2, 'a', 3]

Предположим, что элементы списка хешируемы.

Уточнение: Результат должен сохранить первый дубликат в списке. Например, [1, 2, 3, 2, 3, 1] становится [1, 2, 3].

Сохраняем ли мы первый из дубликатов, или последний, или где-то посередине? например, [1,2,3,2,3,1], становится ли это [1,2,3], [2,3,1] или что-то еще?

Chris Jester-Young 18.09.2008 05:29

Как применить к чему-либо тег "Домашнее задание"? Когда он говорит, что элементы являются хешируемыми, ваш профессор просит вас поместить записи в хеш-таблицу, тогда легко увидеть, сталкивались ли вы с ними раньше, когда вы проходите по списку.

Karl 12.11.2008 03:06

Бенчмарк и четкий ответ здесь.

e-satis 18.09.2008 15:54
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
44
3
31 252
27

Ответы 27

У меня нет опыта работы с python, но алгоритм заключался бы в том, чтобы отсортировать список, затем удалить дубликаты (сравнивая с предыдущими элементами в списке) и, наконец, найти позицию в новом списке, сравнив со старым списком.

Более длинный ответ: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560

Я не проводил никаких тестов, но одним из возможных алгоритмов может быть создание второго списка и повторение первого списка. Если элемента нет во втором списке, добавьте его во второй список.

x = [1, 1, 2, 'a', 'a', 3]
y = []
for each in x:
    if each not in y:
        y.append(each)

Я считаю, что ваше использование имени переменной "each" действительно сбивает с толку, вероятно, потому, что во многих языках это ключевое слово. Намного понятнее использовать item или просто i.

rjmunro 12.11.2008 04:00

«i» для меня означает индекс - мы не перебираем индексы, мы перебираем объекты. Я бы предпочел item, но я не считаю «каждый» плохим - просто потому, что это ключевое слово на другом языке, зачем его здесь не использовать. Подсветка синтаксиса (как показано выше) отлично подходит ...

Matthew Schinckel 26.11.2008 05:30

В каких языках кроме AppleScript используется слово «каждый» в качестве ключевого слова?

Matthew Schinckel 26.11.2008 05:33

Вы должны были использовать набор. Вряд ли это будет самым быстрым.

Marcin 18.11.2012 20:38

Марчин: «... при сохранении порядка».

Matthew Schinckel 19.11.2012 05:15

Взято из http://www.peterbe.com/plog/uniqifiers-benchmark

def f5(seq, idfun=None):  
    # order preserving 
    if idfun is None: 
        def idfun(x): return x 
    seen = {} 
    result = [] 
    for item in seq: 
        marker = idfun(item) 
        # in old Python versions: 
        # if seen.has_key(marker) 
        # but in new ones: 
        if marker in seen: continue 
        seen[marker] = 1 
        result.append(item) 
    return result

он медленнее, чем соответствующая версия на месте (по крайней мере, для некоторых входов). См. stackoverflow.com/questions/89178/#282589

jfs 12.11.2008 03:26

>>> def unique(list):
...   y = []
...   for x in list:
...     if x not in y:
...       y.append(x)
...   return y

Чтобы объяснить, почему: поиск x в структуре списка (y) - это O (n), а поиск x в наборе (или словаре) - O (1).

Hamish Downer 27.09.2008 20:17

Что будет быстрее всего, зависит от того, какой процент дубликатов в вашем списке. Если это почти все дубликаты и несколько уникальных элементов, создание нового списка, вероятно, будет быстрее. Если это в основном уникальные элементы, удаление их из исходного списка (или копии) будет быстрее.

Вот один для изменения списка на месте:

def unique(items):
  seen = set()
  for i in xrange(len(items)-1, -1, -1):
    it = items[i]
    if it in seen:
      del items[i]
    else:
      seen.add(it)

Итерация в обратном направлении по индексам гарантирует, что удаление элементов не повлияет на итерацию.

Это дает разные результаты по сравнению с другими решениями (OP не указал, какое из них является правильным), что касается того, какой дубликат сохранить. Это решение: [1, 2, 1] -> [2, 1] Другие решения: [1, 2, 1] -> [1, 2]

James Hopkin 18.09.2008 13:24

Я добавил пояснение по этому поводу в текст вопроса.

J Miller 18.09.2008 15:50

O (n), если dict - хэш, O (nlogn), если dict - дерево, и простой, фиксированный. Спасибо Мэтью за предложение. Извините, я не знаю основных типов.

def unique(x):    
  output = []
  y = {}
  for item in x:
    y[item] = ""

  for item in x:
    if item in y:
      output.append(item)

  return output

К вашему сведению, вы также можете сделать это с помощью набора, поэтому вам не нужно устанавливать его равным пустой строке.

Jason Baker 18.09.2008 06:42

def unique(items):
    found = set([])
    keep = []

    for item in items:
        if item not in found:
            found.add(item)
            keep.append(item)

    return keep

print unique([1, 1, 2, 'a', 'a', 3])

set () лучше, чем set ([]).

Constantin 29.09.2008 19:22

Алгоритмы на месте работают быстрее. См. Ответы Джеймса и мои.

jfs 12.11.2008 03:44

Это старый поток, но если вы сделаете методы add () и append () локальной функцией (поместите add = found.add и app = keep.append перед циклом, а затем используйте add(item) и app(item), то это будет самым быстрым на сегодняшний день. Причина в том, что использование словаря был быстрее, потому что не требовал поиска атрибутов для каждого добавления и добавления.Только мои два цента.

Justin Peel 23.11.2010 02:22

Если вы потом поместите его в список, вы получите еще одно улучшение скорости. С учетом всех изменений скорость увеличивается почти вдвое. Смотрите мое сравнение ниже на этой странице.

Michael 27.12.2013 11:41

@JustinPeel: вы пробовали запустить эталон?

jfs 13.01.2015 10:18

Зачем вообще нужен set()? Почему недостаточно проверить членство item in items в сравнении с keep[]?

so.very.tired 23.02.2018 20:50

@ so.very.tired Поскольку keep - это список, и проверка принадлежности к списку занимает в среднем линейное время по длине списка. Между тем, проверка принадлежности к набору занимает в среднем постоянное время (см. это). Когда дело доходит до производительности, использование соответствующих структур данных является решающим фактором. В любом случае этот ответ устарел. Попробуйте вместо этого этот вопрос.

gblomqvist 15.01.2020 04:15

Вы можете сделать что-то действительно классное на Python, чтобы решить эту проблему. Вы можете создать понимание списка, которое будет ссылаться на себя при создании. Следующее:

   # remove duplicates...
   def unique(my_list):
       return [x for x in my_list if x not in locals()['_[1]'].__self__]

Обновлено: Я удалил "себя", и он работает в Mac OS X, Python 2.5.1.

_ [1] - это «секретная» ссылка Python на новый список. Вышеизложенное, конечно, немного беспорядочно, но вы можете адаптировать его под свои нужды по мере необходимости. Например, вы можете написать функцию, которая возвращает ссылку на понимание; это было бы больше похоже на:

return [x for x in my_list if x not in this_list()]

Данный пример не компилируется для меня - завершающий ".__ self__" недействителен [[Linux 2.6 w / Python 2.5.1]]

Kevin Little 18.09.2008 06:19

Святая корова, вы превращаете Python в Perl с помощью волшебного подчеркивания. Просто сказать нет.

Parand 13.03.2009 06:49

>>> x=[1,1,2,'a','a',3]
>>> y = [ _x for _x in x if not _x in locals()['_[1]'] ]
>>> y
[1, 2, 'a', 3]


"locals () ['_ [1]']" - это "секретное имя" создаваемого списка.

Наличие _ [1] local не гарантируется языком.

Constantin 29.09.2008 19:20

«<item> в <list>» равно O (n), поэтому это медленно.

Charles Duffy 24.11.2008 13:41

Один лайнер:

new_list = reduce(lambda x,y: x+[y][:1-int(y in x)], my_list, [])

Обязательно ли дубликаты должны быть в списке на первом месте? Нет никаких накладных расходов при поиске элементов, но есть немного больше накладных расходов при добавлении элементов (хотя накладные расходы должны быть O (1)).

>>> x  = []
>>> y = set()
>>> def add_to_x(val):
...     if val not in y:
...             x.append(val)
...             y.add(val)
...     print x
...     print y
... 
>>> add_to_x(1)
[1]
set([1])
>>> add_to_x(1)
[1]
set([1])
>>> add_to_x(1)
[1]
set([1])
>>> 

has_key в Python - O (1). Вставка и извлечение из хэша также O (1). Дважды перебирает n элементов, поэтому O (n).

def unique(list):
  s = {}
  output = []
  for x in list:
    count = 1
    if (s.has_key(x)):
      count = s[x] + 1

    s[x] = count
  for x in list:
    count = s[x]
    if (count > 0):
      s[x] = 0
      output.append(x)
  return output

Один проход.

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

new_list = []
prev = None

while 1:
    try:
        i = a.pop(0)
        if i != prev:
            new_list.append(i)
        prev = i
    except IndexError:
        break

Требуется отсортированный ввод, не так ли?

Constantin 29.09.2008 19:20

С помощью:

lst = [8, 8, 9, 9, 7, 15, 15, 2, 20, 13, 2, 24, 6, 11, 7, 12, 4, 10, 18, 13, 23, 11, 3, 11, 12, 10, 4, 5, 4, 22, 6, 3, 19, 14, 21, 11, 1, 5, 14, 8, 0, 1, 16, 5, 10, 13, 17, 1, 16, 17, 12, 6, 10, 0, 3, 9, 9, 3, 7, 7, 6, 6, 7, 5, 14, 18, 12, 19, 2, 8, 9, 0, 8, 4, 5]

И используя модуль timeit:

$ python -m timeit -s 'import uniquetest' 'uniquetest.etchasketch(uniquetest.lst)'

И так далее для различных других функций (которые я назвал в честь их плакатов), у меня были следующие результаты (на моем Intel MacBook Pro первого поколения):

Allen:                  14.6 µs per loop [1]
Terhorst:               26.6 µs per loop
Tarle:                  44.7 µs per loop
ctcherry:               44.8 µs per loop
Etchasketch 1 (short):  64.6 µs per loop
Schinckel:              65.0 µs per loop
Etchasketch 2:          71.6 µs per loop
Little:                 89.4 µs per loop
Tyler:                 179.0 µs per loop

[1] Обратите внимание, что Аллен изменяет список на месте - я считаю, что это исказило время, поскольку модуль timeit запускает код 100000 раз, и 99999 из них находятся в списке без дублирования.


Резюме: Прямая реализация с наборами побеждает запутанные однострочники :-)

Джеймс предложил более быструю версию. См. stackoverflow.com/questions/89178/#91430

jfs 12.11.2008 03:16

Удар для использования OSX. ;-)

geowar 12.02.2013 04:48

Не знаю, быстро это или нет, но, по крайней мере, все просто.

Просто преобразуйте его сначала в набор, а затем снова в список

def unique(container):
  return list(set(container))

Это не сохраняет порядок.

Eli Courtwright 18.09.2008 17:17

Это самый быстрый метод на месте, который я нашел (при наличии большого количества дубликатов):

def unique(l):
    s = set(); n = 0
    for x in l:
        if x not in s: s.add(x); l[n] = x; n += 1
    del l[n:]

Это на 10% быстрее, чем реализация Аллена, на которой она основана (синхронизировано с timeit.repeat, JIT скомпилировано psyco). Он сохраняет первый экземпляр любого дубликата.

repton-infinity: Мне было бы интересно, можете ли вы подтвердить мои сроки.

Словари немного быстрее наборов. Смотрите мой ответ stackoverflow.com/questions/89178/#282589

jfs 12.11.2008 03:14

Здесь есть несколько отличных и эффективных решений. Однако для тех, кто не интересуется самым эффективным решением O(n), я бы выбрал простое однострочное решение O(n^2*log(n)):

def unique(xs):
    return sorted(set(xs), key=lambda x: xs.index(x))

или более эффективное двухслойное решение O(n*log(n)):

def unique(xs):
    positions = dict((e,pos) for pos,e in reversed(list(enumerate(xs))))
    return sorted(set(xs), key=lambda x: positions[x])

Этот код трудно понять, и вы говорите, что он менее эффективен, чем другие решения, уже представленные здесь. Так зачем тебе это делать?

J Miller 18.09.2008 21:40

Я считаю, что это легко понять; передача лямбда-функции в качестве ключевого параметра сортировки - это действительно канонический способ сортировки списка в Python. Большая часть моей работы с Python включает создание отчетов по спискам статистики, и поэтому мне кажется, что это самый простой и самый питонический подход.

Eli Courtwright 19.09.2008 16:51

Хотя я согласен, что ваше решение является кратким, вопрос задан для самого быстрого алгоритма, а не для самого Pythonic.

J Miller 02.10.2008 02:30

Если вы удалите пустой список из вызова set () в ответе Terhost, вы получите небольшой прирост скорости.

Изменять: found = set ([])
к: найдено = набор ()

Однако вам совсем не нужен набор.

def unique(items):
    keep = []

    for item in items:
        if item not in keep:
            keep.append(item)

    return keep

Используя timeit, я получил следующие результаты:

с комплектом ([]) - 4.97210427363
с комплектом () - 4.65712377445
без набора - 3,44865284975

да, когда у вас мало данных, держу пари, что установленный внутренний механизм работает медленнее, чем итерация по списку. Но если у вас есть элемент maaaaaaaaaaany, я думаю, что набор будет быстрее. Или какой смысл в этих структурах данных ;-)

e-satis 24.09.2008 01:21

Обязательная вариация на базе генератора:

def unique(seq):
  seen = set()
  for x in seq:
    if x not in seen:
      seen.add(x)
      yield x

a = [1,2,3,4,5,7,7,8,8,9,9,3,45]

def unique (l):

ids = {}
for item in l:
    if not ids.has_key(item):
        ids[item]=item
return  ids.keys()

распечатать

распечатать уникальный (а)

----------------------------

Вставка элементов займет theta (n) получение, если элемент выходит или нет, займет постоянное время тестирование всех предметов потребует также тета (n) Итак, мы можем видеть, что это решение будет принимать theta (n) Имейте в виду, что словарь в Python реализован с помощью хеш-таблицы

В вопросах написано "при сохранении порядка". Словарь Python не сохраняет порядок.

jfs 24.11.2008 14:28

Обновлять: на Python3.7 +:

>>> list(dict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']

old answer:

Вот самое быстрое решение (для следующего ввода):

def del_dups(seq):
    seen = {}
    pos = 0
    for item in seq:
        if item not in seen:
            seen[item] = True
            seq[pos] = item
            pos += 1
    del seq[pos:]

lst = [8, 8, 9, 9, 7, 15, 15, 2, 20, 13, 2, 24, 6, 11, 7, 12, 4, 10, 18, 
       13, 23, 11, 3, 11, 12, 10, 4, 5, 4, 22, 6, 3, 19, 14, 21, 11, 1, 
       5, 14, 8, 0, 1, 16, 5, 10, 13, 17, 1, 16, 17, 12, 6, 10, 0, 3, 9, 
       9, 3, 7, 7, 6, 6, 7, 5, 14, 18, 12, 19, 2, 8, 9, 0, 8, 4, 5]
del_dups(lst)
print(lst)
# -> [8, 9, 7, 15, 2, 20, 13, 24, 6, 11, 12, 4, 10, 18, 23, 3, 5, 22, 19, 14, 
#     21, 1, 0, 16, 17]

Поиск в словаре выполняется немного быстрее, чем в Python 3.

Не могли бы вы объяснить, почему в этом случае поиск в словаре выполняется быстрее, чем проверка на членство в наборе?

Stephen Emslie 03.01.2012 14:01

@ Стивен Эмсли: не знаю. Это может быть эталонный артефакт. Попробуй сам. Чистое предположение: словарь - это фундаментальная структура данных для CPython (пространства имен, классы реализованы / были реализованы через словари), поэтому словари более настроены / оптимизированы, чем наборы.

jfs 03.01.2012 15:18

Хорошее время, но неверный вывод. Время показывает только то, что доступ оператора, такой как d[k] = v, быстрее, чем доступ к вызову метода, такой как d.__setitem__(k, v), даже если последний был предварительно привязан с использованием d_setitem = d.__setitem__, а затем времени d_setitem(k, v).

Raymond Hettinger 27.12.2013 12:12

Используя Python 3.4, я попробовал ваш тестовый сценарий; и функция, использующая набор, последовательно немного быстрее.

Honest Abe 13.01.2015 00:29

@jfs: В версии 3.6+ это можно сделать намного проще и даже быстрее, упростив до def del_dups(seq): seq[:] = dict.fromkeys(seq) (для модификации на месте) или def del_dups(seq): return list(dict.fromkeys(seq)) для создания новой копии без дубликатов.

ShadowRanger 10.12.2020 06:12

Порядок dict @ShadowRanger гарантируется языком только начиная с Python 3.7. Хотя это нормально для CPython 3.6+, реализаций Pypy. dict.fromkeys - это O (n) в алгоритме памяти, в отличие от решения O (k) памяти в ответе

jfs 10.12.2020 19:24

Прошу проигнорировать последнее замечание по поводу памяти (моя ошибка)

jfs 10.12.2020 19:31

@jfs: True (при гарантированном заказе). Я обычно включаю эту квалификацию; забыл на этот раз. Есть только один поддерживающий интерпретатор 3.6, помимо тех двух, о которых я знаю (Stackless Python), не знаю, какие гарантии у него есть. Тем не менее, в мы есть некоторые гарантии порядка вставки, сделанные в 3.6 из-за PEP 468 и PEP 520, которые намного проще удовлетворить, если dict упорядочены вставкой, когда никакие удаления не выполняются (единственная причина, по которой они не гарантировали это в 3.6, была потому что они не были уверены, хотят ли они ограничить себя, гарантируя, что пропуски удаления не будут заполнены новыми элементами).

ShadowRanger 10.12.2020 19:35

Поскольку такое использование dict.fromkeys по определению не удаляет из результирующего dict перед вставкой новых ключей (оно делает dict, неглубоко копирует ключи, отбрасывает его), скорее всего работает в интерпретаторах 3.6, которые не гарантируют этого, но да , не гарантируется до 3.7.

ShadowRanger 10.12.2020 19:36

На месте однострочник для этого:

>>> x = [1, 1, 2, 'a', 'a', 3]
>>> [ item for pos,item in enumerate(x) if x.index(item)==pos ]
[1, 2, 'a', 3]

Привет, Марио, как это работает, объясните, пожалуйста, я понял, что индекс возвращает только одно значение, поэтому он уникален?

James 08.12.2013 09:40

Метод list.index (item) возвращает позицию элемента первый, найденного в списке, и, сравнивая это с фактической позицией элемента (из enumerate), мы можем определить, является ли этот элемент первым или нет, сохраняя только первая встреча.

Mario Ruggier 29.01.2014 15:08

спасибо, это очень элегантное решение.

James 29.01.2014 16:58

Очень красивое решение. Но действительно ли это на месте? Когда вы используете понимание списка, разве не создается новый список?

lifebalance 11.10.2020 07:09

Удалите дубликаты и сохраните порядок:

Это быстрый двухстрочный редактор, который использует встроенные функции понимания списков и словарных статей.

x = [1, 1, 2, 'a', 'a', 3]

tmpUniq = {} # temp variable used below 
results = [tmpUniq.setdefault(i,i) for i in x if i not in tmpUniq]

print results
[1, 2, 'a', 3]

Функция dict.setdefaults () возвращает значение, а также добавляет его к temp dict непосредственно в понимании списка. Использование встроенных функций и хэшей dict будет работать для максимальной эффективности процесса.

Вот два рецепта из документации itertools:

def unique_everseen(iterable, key=None):
    "List unique elements, preserving order. Remember all elements ever seen."
    # unique_everseen('AAAABBBCCDAABBB') --> A B C D
    # unique_everseen('ABBCcAD', str.lower) --> A B C D
    seen = set()
    seen_add = seen.add
    if key is None:
        for element in ifilterfalse(seen.__contains__, iterable):
            seen_add(element)
            yield element
    else:
        for element in iterable:
            k = key(element)
            if k not in seen:
                seen_add(k)
                yield element

def unique_justseen(iterable, key=None):
    "List unique elements, preserving order. Remember only the element just seen."
    # unique_justseen('AAAABBBCCDAABBB') --> A B C D A B
    # unique_justseen('ABBCcAD', str.lower) --> A B C A D
    return imap(next, imap(itemgetter(1), groupby(iterable, key)))

Это может быть самый простой способ:

list(OrderedDict.fromkeys(iterable))

Начиная с Python 3.5, OrderedDict теперь реализован на C, поэтому он стал самым коротким, чистым и быстрым.

Элегантно, но, к сожалению, на порядок медленнее, чем самое быстрое решение, и, на удивление, одно из самых медленных решений в целом. OrderedDict кажется настоящим убийцей производительности.

Michael 26.12.2013 20:04

Возможно, если OrderedSet когда-нибудь станет встроенным, у нас будет очень быстрое решение

jamylak 01.01.2015 10:02

Это самый быстрый, сравнивая все материалы из этого длительное обсуждение и других ответов, приведенных здесь, со ссылкой на этот ориентир. Это еще на 25% быстрее, чем самая быстрая функция из обсуждаемых, f8. Спасибо Дэвиду Кирби за идею.

def uniquify(seq):
    seen = set()
    seen_add = seen.add
    return [x for x in seq if x not in seen and not seen_add(x)]

Некоторое сравнение времени:

$ python uniqifiers_benchmark.py 
* f8_original 3.76
* uniquify 3.0
* terhorst 5.44
* terhorst_localref 4.08
* del_dups 4.76

Я не вижу сравнения времени с решениями из лучших ответов здесь. По моему опыту, явные циклы быстрее, чем понимание списков в CPython (по крайней мере, для каждого конкретного случая требуется эталонный тест).

jfs 26.12.2013 21:56

Я добавил тайминги выше. Основные накладные расходы в представленных решениях - это поиск свойств для добавления, добавления и т. д., Но даже если вы откажетесь от этого, понимание списка будет примерно на 25% быстрее, чем terhorst_localreferences.

Michael 27.12.2013 11:37

не могли бы вы включить в свой ответ полный код теста? Я не вижу terhorst (или любого другого соответствующего кода) в файл, который вы связали.

jfs 27.12.2013 12:31
pastebin.com/C5SQmT1R
Michael 27.12.2013 20:52

x = [] # Your list  of items that includes Duplicates

# Assuming that your list contains items of only immutable data types

dict_x = {} 

dict_x = {item : item for i, item in enumerate(x) if item not in dict_x.keys()}
# Average t.c. = O(n)* O(1) ; furthermore the dict comphrehension and generator like behaviour of enumerate adds a certain efficiency and pythonic feel to it.

x = dict_x.keys() # if you want your output in list format 

Что может пойти не так с элементами изменяемых типов?

ByteEater 11.03.2021 03:36

Это не работает так, как вы думаете. if item not in dict_x.keys() проверяет ключи исходного пустого dict_x, а не создаваемого словаря. Это всегда правда. Дубликаты удаляются просто потому, что попытки создать дубликат ключа игнорируются.

Barmar 11.03.2021 03:38

Почему вы используете enumerate()?

Barmar 11.03.2021 03:40

@ByteEater Мутабельные типы нельзя использовать в качестве ключей словаря.

Barmar 11.03.2021 03:42

@Bramar, возможно, он попытался включить изменяемые типы, написав i: item вместо item : item (хотя достаточно одного имени для пары), а затем применил .values() вместо .keys() в обоих местах. Но это не сработает из-за вашего первого комментария.

ByteEater 11.03.2021 03:50

Другие вопросы по теме