Удалить дубликаты в списке объектов с помощью Python

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

Затем я хочу проверить, есть ли в моем списке объектов дубликаты каких-либо записей в базе данных, и если да, то удалить эти элементы из списка, прежде чем добавлять их в базу данных.

Я видел решения для удаления дубликатов из списка, например: myList = list(set(myList)), но я не уверен, как это сделать со списком объектов?

Мне также нужно поддерживать порядок в моем списке объектов. Я также подумал, может, я мог бы использовать difflib, чтобы проверить различия в заголовках.


person imns    schedule 12.11.2010    source источник
comment
оставив оригинал, что это значит? потому что, как вы сказали, вы хотите поддерживать порядок в списке, чтобы первое появление повторяющегося объекта в списке было исходным, верно?   -  person mouad    schedule 13.11.2010
comment
да, я просто имел ввиду, что хочу удалить все дубликаты, кроме оригинала. @ S.Lott, я много искал и ничего не нашел, поэтому и пришел сюда. Вы можете привести пример, решающий именно эту проблему? Я был бы рад это увидеть.   -  person imns    schedule 13.11.2010
comment
stackoverflow.com/.   -  person S.Lott    schedule 15.11.2010


Ответы (7)


set(list_of_objects) удалит дубликаты только в том случае, если вы знаете, что такое дубликат, то есть вам нужно определить уникальность объекта.

Для этого вам нужно сделать объект хешируемым. Вам нужно определить как __hash__, так и __eq__ метод, вот как:

http://docs.python.org/glossary.html#term-hashable

Хотя вам, вероятно, потребуется только определить __eq__ метод.

РЕДАКТИРОВАТЬ: как реализовать метод __eq__:

Как я уже упоминал, вам нужно знать определение уникальности вашего объекта. Предположим, у нас есть Книга с атрибутами author_name и title, комбинация которых уникальна (таким образом, у нас может быть много книг, созданных Стивеном Кингом, и много книг с названием «Сияние», но только одна книга под названием «Сияние» Стивена Кинга), затем реализация как следует:

def __eq__(self, other):
    return self.author_name==other.author_name\
           and self.title==other.title

Точно так же я иногда реализую метод __hash__:

def __hash__(self):
    return hash(('title', self.title,
                 'author_name', self.author_name))

Вы можете проверить, что если вы создадите список из 2 книг с одинаковым автором и названием, объекты книги будут одинаковыми (с оператором is) и (с оператором ==). Кроме того, при использовании set() будет удалена одна книга.

РЕДАКТИРОВАТЬ: это один из моих старых ответов, но я только сейчас заметил, что в нем есть ошибка, исправленная зачеркиванием в последнем абзаце: объекты с одинаковым hash() не будут давать True по сравнению с is. Однако хэшируемость объектов используется, если вы собираетесь использовать их как элементы набора или как ключи в словаре.

person vonPetrushev    schedule 13.11.2010
comment
Хорошо, я не знала о __hash__ и __eq__. Есть примеры того, как реализовать __eq__? - person imns; 14.11.2010
comment
вам нужно убедиться, что класс такой же, или поле не будет доступно, поэтому eq также необходимо сделать self.__class__ == other.__class__ and self.author_name==other.author_name\ and self.title==other.title - person Mahesh; 21.06.2019

Поскольку они не могут быть хешированы, вы не можете использовать набор напрямую. Заголовки хоть должны быть.

Вот первая часть.

seen_titles = set()
new_list = []
for obj in myList:
    if obj.title not in seen_titles:
        new_list.append(obj)
        seen_titles.add(obj.title)

Однако вам нужно будет описать, какую базу данных / ORM и т. Д. Вы используете для второй части.

person aaronasterling    schedule 12.11.2010
comment
Я использую mysql с sqlobject. - person imns; 13.11.2010
comment
@bababa, пожалуйста, обновите вопрос, чтобы его увидели и другие люди. - person aaronasterling; 13.11.2010
comment
@bababa, я не вижу хорошего способа сделать это с помощью sqlobject (т.е. без извлечения каждого объекта из БД в одном запросе или выполнения одного запроса для каждого объекта), поэтому я немного подожду, а затем опубликую это, если кто-то не Я не знаю sqlobject лучше меня. - person aaronasterling; 13.11.2010

Это кажется довольно минимальным:

new_dict = dict()
for obj in myList:
    if obj.title not in new_dict:
        new_dict[obj.title] = obj
person hughdbrown    schedule 13.11.2010

Для этого нужны как __hash__, так и __eq__.

__hash__ необходим для добавления объекта в набор, поскольку наборы Python реализованы как хэш-таблицы. По умолчанию неизменяемые объекты, такие как числа, строки и кортежи, являются хешируемыми.

Однако хеш-коллизии (хеширование двух разных объектов с одним и тем же значением) неизбежны из-за принципа «ящика». Таким образом, два объекта нельзя отличить только по их хешу, и пользователь должен указать свою собственную __eq__ функцию. Таким образом, фактическая хэш-функция, предоставляемая пользователем, не имеет решающего значения, хотя лучше всего попытаться избежать коллизий хеширования для повышения производительности (см. Что является правильным и хороший способ реализовать __hash __ ()?).

person qwr    schedule 26.11.2018

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

seen = {}
for obj in objList:
    if obj["key-property"] in seen.keys():
        objList.remove(obj)
    else:
        seen[obj["key-property"]] = 1
person binW    schedule 17.12.2019
comment
Это работает только в том случае, если objList содержит сопоставимые объекты (т.е. реализует метод eq). Для получения дополнительной информации см. stackoverflow.com/a/11456817/290588 Создание дедуплицированного списка будет работать для объектов, которые не реализуют < b> уравнение. - person LietKynes; 03.06.2021

Если вы хотите сохранить исходный порядок, используйте его:

seen = {}
new_list = [seen.setdefault(x, x) for x in my_list if x not in seen]

Если вы не заботитесь о заказе, используйте его:

new_list = list(set(my_list))
person Amir    schedule 20.11.2016

Это довольно простые друзья: -

a = [5,6,7,32,32,32,32,32,32,32,32]

а = список (набор (а))

печать (а)

[5,6,7,32]

Это оно ! :)

person Spiderman    schedule 17.03.2011
comment
Невозможно сделать это для списка, содержащего объекты. - person Brad Bird; 21.09.2014