Как я могу заставить мои подклассы Python `set` и` frozenset` сохранять свои типы при участии в бинарных операциях?

1

У меня есть некоторые подклассы set и frozenset, OCDSet и OCDFrozenSet соответственно. Когда я использую их вместе с экземплярами своих классов-предков в двоичных операциях, классы предков доминируют над типом результата - под этим я имею в виду, когда я делаю что-то вроде вычитания OCDFrozenSet из frozenset, я получаю frozenset... но тот же истинно, если я frozenset типы в операции (т.е. frozenset из OCDFrozenSet.

Вот так:

Изображение 174551

... что особенно противоречиво досадно мне, тот факт, что использование -= (subtract-in-place) мутирует тип существующего экземпляра!

Мои знания о том, как справляться с подобными вещами, берутся исключительно из C++, где тип операции - это упущенный вывод, который явно указан в (вероятной шаблонизированной) функции оператор-перегрузка; в Python система типов часто гораздо более неявная, но она не настолько непредсказуема, поскольку эта операция на месте заставила бы меня теперь верить.

Итак, что является наиболее целесообразным способом решения этого вопроса? Я предполагаю, что он включает в себя переопределение некоторых методов с двойным подчеркиванием в интересующих подклассах?

Теги:
types
subclassing
immutability
binary-operators

1 ответ

2
Лучший ответ

Операции на месте не гарантируют, что они будут обновлять объект на месте, он полностью зависит от типа объекта.

Tuple, frozenset и т.д. Являются неизменяемыми типами, поэтому их невозможно обновить на месте.

Из справочника библиотек операторов на месте:

Для неизменяемых целей, таких как строки, числа и кортежи, обновленное значение вычисляется, но не возвращается обратно к входной переменной.

Аналогичным образом в документах frozenset также упоминается одно и то же о работе на месте [ источник ]:

В следующей таблице перечислены операции, доступные для набора, которые не применяются к неизменяемым экземплярам frozenset.


Теперь, поскольку ваш OCDFrozenSet не реализует __isub__, он будет __sub__ методу __sub__ который вернет тип базового класса frozenset. Базовый класс используется, потому что Python не имеет представления о аргументах, которые ваш базовый класс ожидал бы от недавно созданного frozenset из операции __sub__.

Что еще более важно, это была ошибка в Python 2, где такая операция возвращала экземпляр подкласса, исправление было портировано только на Python 3, но для предотвращения нарушения существующих систем.


Чтобы получить ожидаемый результат, вы можете предоставить необходимые методы в вашем подклассе:

class OCDFrozenSet(frozenset):
    def __sub__(self, other):
        return type(self)(super().__sub__(other))

    def __rsub__(self, other):
        return type(self)(super().__rsub__(other))
  • 0
    Ок вау спасибо! - хотя (и исправьте меня, если я ошибаюсь), поведение, которое я вижу (где меняется тип экземпляра, в котором использовался оператор на месте), означает, что обновленное значение фактически присваивается обратно входная переменная, в отличие от ссылки? Кроме того, ваш ответ подразумевает, что я мог бы захотеть реализовать __isub__(…) на изменчивых изменчивых подклассах - так ли это?
  • 1
    @ fish2000 Для ввода переменной, да, но это больше не тот же объект, то есть id() будет другим, и другие ссылки на объект также не будут затронуты. Это просто совершенно новый объект, который присваивается переменной. Проверьте: repl.it/@ashwinichaudhary/WaryThisThing . Да, __isub__ подходит для вашего случая.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню