Какие классы не могут быть подклассами?

Есть ли какое-либо правило о том, какие встроенные и стандартные библиотечные классы не являются подклассами ("final")?

Что касается Python 3.3, вот несколько примеров:

  • bool
  • function
  • operator.itemgetter
  • slice

Я нашел вопрос, который касается с реализацией «финальных» классов, как на C, так и на чистом Python.

Я хотел бы понять, какие причины могут объяснить, почему класс выбран в качестве "финального" в первую очередь.


person max    schedule 08.04.2012    source источник
comment
NoneType — еще один пример.   -  person Duncan    schedule 08.04.2012
comment
Может ли конечный класс в одной реализации Python быть подклассом в другой реализации? Я надеюсь, что кто-то может подтвердить, что этого никогда не происходит. В противном случае код, написанный для одной реализации, может сломаться при переносе на другую (тоже очень болезненно: представьте, если бы кто-то создал подкласс function, и теперь ему нужно провести рефакторинг кода, чтобы избежать этого наследования).   -  person max    schedule 10.04.2012
comment
Обратите внимание, что PyPy также отказывается создавать подклассы для ваших четырех примеров... даже несмотря на то, что у него нет ограничения CPython. У них может быть причина, задокументированная в их кодовой базе.   -  person mbauman    schedule 10.04.2012
comment
NotImplementedType (т. е. type(NotImplemented)) и ellipsis (т. е. type(...)) — еще два примера. Как и в случае с None, нет причин иметь более одного экземпляра этих классов, и если бы это было разрешено, было бы более неудобно проверять их (if x is None должен был бы стать if isinstance(x, type(None))). Я полагаю, в принципе, вы могли бы получить полный список, просматривая источник значения tp_flags в определениях типов.   -  person James    schedule 10.04.2012
comment
Мэтт Б: PyPy нужно запускать весь код Python. Даже RPython может работать на интерпретаторе python (который из всего, что я видел, кажется УЖАСНОЙ идеей и головной болью для кода, но они заставляют его работать). Поэтому было бы бессмысленно, если бы они разрешали код, который нельзя запускать на Python.   -  person Garrett Berg    schedule 10.04.2012
comment
Существуют ли какие-либо классы, которые вы не можете подклассировать, где создание подклассов было бы полезным и не ничего не ломало? Создание подкласса синглтона может привести к поломке, а создание подкласса function кажется ненужным — вместо этого вы можете использовать магический метод __call__. Если ответ «Нет», то правило состоит в том, что только классы, которые могут быть полезными для подклассов, являются подклассами.   -  person agf    schedule 11.04.2012
comment
@agf: см. этот пост, чтобы узнать, почему вы хотите создать подкласс itemgetter ; и этот пост для почему вы можете захотеть создать подкласс function (это старый, поэтому некоторые из этих аргументов могут не применяться).   -  person max    schedule 11.04.2012
comment
@max Я не вижу в этом причины для подкласса itemgetter - для текущего поведения равенства нет варианта использования (поскольку, если вы хотите такое поведение, вы используете is), поэтому нет причин для подкласса, а не для изменения поведения, кроме как обходной путь. Что касается function, все это либо возможно без создания подкласса function, либо лучше сделать с вызываемым экземпляром пользовательского класса. Да, если бы все было подклассом, у нас было бы больше гибкости, но гибкость без варианта использования не является причиной в Python.   -  person agf    schedule 11.04.2012
comment
@agf: на itemgetter согласился на 100%, что более разумно (и намного проще!) Исправить поведение равенства, чем выполнять работу, необходимую для разрешения создания подклассов. Обсуждение подкласса function было слишком сложным, чтобы я мог полностью его проследить; если вы считаете, что вариант использования для создания подклассов слаб, я бы поверил вам на слово. В целом, кажется, что ваши комментарии, похоже, напрямую связаны с двумя причинами, которые мы определили до сих пор: сломать что-то ‹=› одноэлементный шаблон; бесполезно ‹=› недостаточный интерес. Итак, я начинаю чувствовать, что мы близки к консенсусу.   -  person max    schedule 11.04.2012
comment
@max Наверное, я пытался сказать, что думаю, что это действительно недостаточная необходимость, а не недостаточный интерес. Отсутствие интереса есть следствие отсутствия потребности. Мне кажется, что лучшим решением этого вопроса было бы, если бы вы переместили свое обновление в ответ, поскольку я не думаю, что мы приблизимся к этому.   -  person agf    schedule 11.04.2012
comment
@agf: понятно. Ваше мнение сильнее, чем просто недостаточный интерес; больше похоже на отсутствие реального варианта использования. Я должен согласиться, основываясь на том, что я видел до сих пор.   -  person max    schedule 11.04.2012
comment
Python 2 xrange, также известный как Python 2 range, также известный как six.moves.xrange, не может быть подклассом.   -  person Bob Stein    schedule 09.06.2017
comment
memoryview не является приемлемым базовым типом   -  person AJNeufeld    schedule 05.07.2018
comment
range встроенная функция является еще одним важным примером, и она обсуждается в этом сообщении: stackoverflow.com/q/30362799/8844500   -  person FraSchelle    schedule 05.01.2021


Ответы (1)


Кажется, есть две причины, по которым класс может быть "окончательным" в Python.

1. Нарушение инварианта класса

Классы, которые следуют шаблону Singleton, имеют инвариант, заключающийся в том, что существует ограниченное (заранее определенное) количество экземпляров. Любое нарушение этого инварианта в подклассе будет несовместимо с целью класса и не будет работать правильно. Примеры:

В этой категории могут быть случаи, отличные от шаблона Singleton, но мне ничего не известно.

2. Нет убедительного варианта использования

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

Примечание 1:

Первоначально я думал, что есть допустимые варианты использования, но просто недостаточный интерес к подклассам function и operator.itemgetter. Спасибо @agf за указание на предлагаемые варианты использования здесь и здесь неубедительны (см. agf комментарии к вопросу).

Заметка 2:

Меня беспокоит то, что другая реализация Python может случайно разрешить создание подкласса для класса final в CPython. Это может привести к непереносимости кода (вариант использования может быть слабым, но кто-то все равно может написать код, который является подклассом function, если его Python поддерживает это). Это можно решить, пометив в документации Python все встроенные и стандартные библиотечные классы, которые не могут быть подклассами, и потребовав, чтобы все реализации следовали поведению CPython в этом отношении.

Заметка 3:

Сообщение, создаваемое CPython во всех вышеперечисленных случаях:

TypeError: type 'bool' is not an acceptable base type

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

TypeError: type 'bool' is final (non-extensible)
person max    schedule 11.04.2012
comment
Конечно, если вы пишете симуляцию кота Шредингера, вы можете захотеть создать подкласс bool, чтобы включить unknown :-P, шучу. - person Endophage; 12.04.2012
comment
@Endophage: Qbit, безусловно, был бы полезным типом, но это не подкласс bool, на самом деле, как раз наоборот, каждое логическое значение является своего рода кубитом! тот, который случается наблюдать. Я бы, наверное, разобрался с этим делом с __subclasscheck__ и друзьями. - person SingleNegationElimination; 12.04.2012
comment
@TokenMacGuy отличное наблюдение! Так почему же у нас нет класса Qbit в Python, подклассом которого является bool! Я требую, чтобы это было так! :-П - person Endophage; 12.04.2012
comment
Но значение кубита всегда будет False или True при наблюдении/тестировании. Кажется, вам просто нужен подкласс bool, который выполняет свои вычисления только при наблюдении.... (LazyBool) - person DylanYoung; 24.01.2020
comment
@SingleNegationElimination @Endophage Может быть, поэтому bool не может быть базовым классом Enum, Гвидо не играет в кости ???? - person Nuno André; 22.03.2021