Заключительные классы в 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
Ответ 2
Вы можете сделать это только через API C. Очистите Py_TPFLAGS_BASETYPE
бит tp_flags
объекта типа.
Вот так: http://svn.python.org/projects/python/trunk/Objects/boolobject.c (vs intobject.c, где Py_TPFLAGS_BASETYPE
).
Ответ 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