Заключительные классы в Python 3.x-то, что Гвидо не говорит мне?

Этот вопрос строится на основе многих предположений. Если одно допущение ошибочно, то все дело падает. Я все еще относительно новичок в Python и только что вступил в любопытную/исследовательскую фазу.

Я понимаю, что Python не поддерживает создание классов, которые нельзя подклассифицировать (заключительные классы). Однако мне кажется, что класс bool в Python не может быть подклассом. Это имеет смысл, когда рассматривается смысл класса bool (поскольку bool должен иметь только два значения: true и false), и я доволен этим. Я хочу знать, как этот класс был отмечен как окончательный.

Итак, мой вопрос:, как именно Guido удалось предотвратить подклассирование bool?

>>> class TestClass(bool):
        pass

Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    class TestClass(bool):
TypeError: type 'bool' is not an acceptable base type

Связанный вопрос: Почему я не могу расширить bool в Python?

Ответ 1

Вы можете легко имитировать тот же эффект от Python 3.x:

class Final(type):
    def __new__(cls, name, bases, classdict):
        for b in bases:
            if isinstance(b, Final):
                raise TypeError("type '{0}' is not an acceptable base type".format(b.__name__))
        return type.__new__(cls, name, bases, dict(classdict))

class C(metaclass=Final): pass

class D(C): pass

даст следующий результат:

Traceback (most recent call last):
  File "C:\Temp\final.py", line 10, in <module>
    class D(C): pass
  File "C:\Temp\final.py", line 5, in __new__
    raise TypeError("type '{0}' is not an acceptable base type".format(b.__name__))
TypeError: type 'C' is not an acceptable base type

Ответ 3

Final и @final типы теперь доступны в typing_extensions.

Я написал статью, охватывающую почти каждую часть этого нового типа: https://sobolevn.me/2018/07/real-python-contants

Некоторые примеры с классами:

from typing_extensions import final

@final
class HRBusinessUnit(AbstractBusinessUnit):
    def grant_permissions(self) -> None:
        self.api.do_some_hr_stuff()


class SubHRBusinessUnit(HRBusinessUnit):  # mypy will raise an error
    def grant_permissions(self) -> None:
        self.api.do_some_it_stuff()

И с константами:

from typing_extensions import Final

DAYS_IN_A_WEEK: Final = 7
DAYS_IN_A_WEEK = 8  # mypy will raise an error

Также у нас есть небольшая библиотека для записи final классов, которые также проверяются во время выполнения! https://github.com/wemake-services/final-class

from final_class import final


@final
class Example(object):  # You won't be able to subclass it!
    ...


class Error(Example):  # Raises 'TypeError'
    ...

Особенности:

  • Нет конфликтов метаклассов
  • Нет времени выполнения
  • Нет зависимостей
  • Тип подсказки включены
  • Разработан, чтобы быть максимально простым

Ответ 4

В Python 3.8 вы можете использовать декоратор final:

from typing import final


@final
class SomeBase:
    ...

В Python 3.6 вы можете заблокировать подклассы без использования метакласса, подобного следующему:

class SomeBase:

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        if cls is not SomeBase:
            raise TypeError("SomeBase does not support polymorphism.  Use composition over inheritance.")


class Derived(SomeBase):
    pass