Які класи не можна підкласувати?


75

Чи існує якесь правило про те, які вбудовані та стандартні класи бібліотеки не підкласуються ("остаточний")?

Щодо Python 3.3, ось кілька прикладів:

  • bool
  • function
  • operator.itemgetter
  • slice

Я знайшов питання, яке стосується реалізації "заключних" класів як на мові C, так і на чистому Python.

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


6
NoneTypeє ще одним прикладом.
Дункан

4
Чи може кінцевий клас в одній реалізації Python бути підкласуваний в іншій реалізації? Сподіваюся, хтось може підтвердити, що цього ніколи не відбувається. В іншому випадку код, написаний для однієї реалізації, може зламатися при перенесенні в іншу (дуже болісно теж: уявіть, якщо хтось здійснив підклас function, і тепер йому потрібно рефакторингувати код, щоб уникнути цього успадкування).
більше

3
Зверніть увагу, що PyPy також відмовляється підкласувати ваші чотири приклади ... навіть незважаючи на те, що він не має обмеження CPython. У них може бути причина, задокументована у їхній кодовій базі.
Matt B.

4
NotImplementedType(тобто, type(NotImplemented)) та ellipsis(тобто, type(...)) - ще два приклади. Мовляв None, немає жодної причини мати більше одного екземпляра цих класів, і якби це було дозволено, було б незручніше перевіряти їх ( if x is Noneпотрібно було б стати if isinstance(x, type(None))). Я припускаю, що, в принципі, ви можете отримати повний список, переглянувши значення джерела tp_flagsу визначеннях типу.
Джеймс,

2
@agf: Перегляньте цю публікацію, чому ви хочете підклас itemgetter; і цей допис про те, чому ви можете захотіти підклас function(цей є старим, тому деякі з цих аргументів можуть не застосовуватися).
більше

Відповіді:


31

Здається, що є дві причини для того, щоб клас був "остаточним" у Python.

1. Порушення класової інваріанти

Класи, які слідують шаблону Синглтона, мають інваріант щодо обмеженої (заздалегідь визначеної) кількості випадків. Будь-яке порушення цього інваріанта в підкласі буде суперечити наміру класу і не працюватиме належним чином. Приклади:

У цій категорії можуть бути випадки, крім шаблону Сінглтона, але я не знаю жодного.

2. Немає переконливого випадку використання

Клас, реалізований на мові C, вимагає додаткової роботи, щоб дозволити підкласифікацію (принаймні в CPython). Робити таку роботу без переконливого випадку не надто привабливо, тому волонтери рідше виступають. Приклади:

Примітка 1:

Спочатку я думав, що існували обґрунтовані випадки використання, але просто недостатній інтерес для підкласифікації functionта operator.itemgetter. Дякую @agf за те, що він зазначив, що запропоновані тут і тут випадки використання не є переконливими (див. Коментарі @agf до питання).

Примітка 2:

Мене турбує те, що інша реалізація Python може випадково дозволити підкласувати клас, який є остаточним у CPython. Це може призвести до непереносного коду (варіант використання може бути слабким, але хтось може все-таки написати код, що підкласи, functionякщо їхній Python це підтримує). Це можна вирішити, позначивши в документації Python всі вбудовані та стандартні класи бібліотеки, які не можна підкласифікувати, та вимагаючи, щоб усі реалізації відповідали поведінці CPython у цьому відношенні.

Примітка 3:

Повідомлення, яке видає CPython у всіх вищезазначених випадках:

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

Це досить загадково, як свідчать численні запитання на цю тему. Я подаю пропозицію додати абзац до документації, що пояснює підсумкові класи, і, можливо, навіть змінити повідомлення про помилку на:

TypeError: type 'bool' is final (non-extensible)

5
Звичайно, якщо ви писали симуляцію кота Шредінгера, ви, можливо, захочете boolвключити в підклас :unknown
P

9
@Endophage: Qbit, безсумнівно, був би корисним типом, але це не підколка bool, насправді, навпаки, кожне булеве значення - це свого роду qbit! той, який випадково спостерігається. Напевно, я б розглядав цю справу разом із __subclasscheck__друзями.
SingleNegationElimination

5
@TokenMacGuy відмінне спостереження! То чому б нам не мати клас Qbit в Python, для якого bool є підкласом! Я вимагаю, щоб так було! :-P
Ендофаг

Але значення Qbit завжди буде False або True при спостереженні / тестуванні. Здається, вам просто потрібен підклас bool, який робить свій "розрахунок" лише за спостереженням .... ( LazyBool)
DylanYoung
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.