Робить визначений користувачем клас визначеного користувачем класу сортувальним, хешируемым


83

Які методи потрібно замінити / реалізувати, коли користувацькі класи класу можна сортувати та / або хешувати в python?

На що слід звертати увагу?

Я набираю текст dir({})у своєму інтерпретаторі, щоб отримати список методів вбудованих диктовок. З них, я припускаю, мені потрібно дещо реалізувати деяку підмножину

['__cmp__', '__eq__', '__ge__', '__gt__', '__hash__', '__le__', '__lt__', '__ne__']

Чи є різниця в тому, які методи повинні бути реалізовані для Python3 на відміну від Python2?


3
Хороша дискусія тут: stackoverflow.com/q/1061283/641766 . Різниця між Python 2.x та 3.x полягає в тому, що __cmp__було видалено.
zeekay

Відповіді:


90

Я майже розмістив це як коментар до інших відповідей, але це насправді відповідь сама по собі.

Щоб ваші предмети можна було сортувати, їх потрібно лише впровадити __lt__. Це єдиний метод, що використовується вбудованим сортуванням.

Інші порівняння functools.total_orderingпотрібні лише у тому випадку, якщо ви дійсно хочете використовувати оператори порівняння зі своїм класом.

Щоб зробити ваші предмети хешируемими, ви впроваджуєте, __hash__як зазначали інші. Ви також повинні реалізовувати __eq__сумісним способом - елементи, які еквівалентні, повинні хешувати однаково.


отже, погана реалізація __lt__може призвести до непередбачуваного сортування python? (наприклад, якщо x .__ lt __ (y) та y .__ lt __ (x))
Matt Fenwick

3
Я не знаю про "непередбачувано", це буде послідовно, якщо подавати точно такий же вхід, але інший порядок введення може призвести до того, що різні елементи будуть в іншому порядку. Так, якщо ви неправильно реалізуєте порівняння, яке використовується для сортування, Python буде сортувати неправильно. Я б порекомендував __key__функцію, яка перетворює екземпляр у кортеж, а потім просто використовуйте це __lt__( self.__key__() < other.__key__()) та __hash__( hash(self.__key__())).
agf

21

Між Python 2 і 3 немає ніякої різниці.

Для сортування:

Вам слід визначити методи порівняння. Це робить ваші предмети сортувальними. Як правило, ви не повинні віддавати перевагу __cmp__().

Зазвичай я використовую декоратор functools.total_ordering.

functools.total_ordering (cls) Враховуючи клас, що визначає один або декілька методів упорядкованого порівняння, цей декоратор класів надає решту. Це спрощує зусилля, необхідні для вказівки всіх можливих розширених операцій порівняння:

Клас повинен визначити один з __lt__(), __le__(), __gt__()або __ge__(). Крім того, клас повинен надати __eq__()метод.

Ви повинні бути обережними, щоб ваші методи порівняння не мали побічних ефектів. (змінити будь-яке значення об'єкта)

Для хешування:

Ви повинні реалізувати __hash__()метод. Я думаю, що найкращий спосіб - це повернення hash(repr(self)), тому ваш хеш був би унікальним.


Приклад functools.total_orderingз документації див. Тут .
Євген Сергєєв

3

Є кілька способів позначення об’єкта сортуванням. Перший - розширене порівняння, що визначається набором функцій:

object.__lt__(self, other)
object.__le__(self, other)
object.__eq__(self, other)
object.__ne__(self, other)
object.__gt__(self, other)
object.__ge__(self, other)

Також можна визначити лише одну функцію:

object.__cmp__(self, other)

І останнє слід визначити, якщо ви хочете визначити власну __hash__функцію. Див. Документ .


6
У Python 3 "[...] __cmp__()спеціальний метод більше не підтримується", див. Відповідний розділ тут .
Євген Сергєєв

-3

__lt__(self,other)Метод реалізації - це відповідь, щоб зробити ваш клас сортувальним.
Його можна використовувати не тільки для вбудованого методу sorted(iterable), але й черги пріоритетів через heapqмодуль.

Крім того, мені не подобається дизайн python, тому багато '__ge__', '__gt__', '__le__', '__lt__', '__ne__'методів зовсім не інтуїтивно зрозумілі !
На противагу цьому Java Interface Comparable<T> (див. Java doc ) повертає від’ємне ціле, нульове або додатне ціле число, оскільки цей об’єкт менше, дорівнює або перевищує вказаний об’єкт, який є прямим і зручним !


9
Більша частина вашої відповіді охоплює вашу думку (яка не повинна бути частиною відповіді).
skyking

@skyking ... хоча я не згоден з думками в цій конкретній відповіді, я вважаю, що думка (з дослідженими та корисними даними, що стосуються питання) є дуже цінною. Помилка у цій відповіді на думку не підтверджує думку відповідними даними. Але розуміння переваг допомагає людям приймати рішення, а тому дуже корисно. Тут відповідь неправильно сформована, оскільки немає корисного та дієвого коду пітона, який би проілюстрував пітонічний спосіб кодування на основі уподобань автора.
Ендрю
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.