Илья прав в том, что если все классы вашей модели являются подклассами Base
, то это подходящий вариант для типа, хинтующего ваш дикт.
Если, однако, вы хотите ввести hint что-то, что получает Base
в качестве аргумента, до того, как вы его создали, то есть что-то вроде следующего:
class DatabaseJanitor:
def __init__(self, base: WhatTypeHere?, engine):
self._base = base
self._engine = engine
def create_metadata(self):
self._base.metadata.create_all(self._engine)
Это немного сложнее... Я немного покопался и вот что нашел для sqlalchemy-1.4.17
.
В sqlalchemy/orm/decl_api.py
можно увидеть следующее:
class DeclarativeMeta(type):
def __init__(cls, classname, bases, dict_, **kw):
...
def declarative_base(
...
metaclass=DeclarativeMeta,
):
...
return registry(
_bind=bind,
metadata=metadata,
class_registry=class_registry,
constructor=constructor,
).generate_base(
mapper=mapper,
cls=cls,
name=name,
metaclass=metaclass,
)
class registry(object):
...
def generate_base(
...
metaclass=DeclarativeMeta,
):
metadata = self.metadata
bases = not isinstance(cls, tuple) and (cls,) or cls
class_dict = dict(registry=self, metadata=metadata)
if isinstance(cls, type):
class_dict["__doc__"] = cls.__doc__
if self.constructor:
class_dict["__init__"] = self.constructor
class_dict["__abstract__"] = True
if mapper:
class_dict["__mapper_cls__"] = mapper
return metaclass(name, bases, class_dict)
Таким образом, declarative_base
вернет экземпляр переданного аргумента ключевого слова metaclass
. Таким образом, Base = declarative_base(metaclass=MyNewMetaclass)
приведет к type(Base) == MyNewMetaclass
.
Кроме того, как видно из registry.generate_base
, все атрибуты нового экземпляра метакласса (например, metadata
) содержатся в class_dict
. Они неявно добавляются как атрибуты к экземпляру во время создания, поэтому они недоступны во время подсказки типа.
(Примечание: я провел небольшое исследование, и оказалось, что, поскольку DeclarativeMeta
подклассы type
, неявно вызывается type.__new__
, который затем добавляет значения в словаре class_dict
в качестве атрибутов к экземпляру DeclarativeMeta
. Я приведу ссылки на некоторые ресурсы для дальнейшего чтения по адресу конец этого ответа.)
Поэтому, даже если вы наберете подсказку о своей функции, например:
class DatabaseJanitor:
def __init__(self, base: DeclarativeMeta, engine):
self._base = base
self._engine = engine
def create_metadata(self):
self._base.metadata.create_all(self._engine)
В create_metadata
подсказка вашего типа будет предупреждать Unresolved attribute reference 'metadata' for class 'DeclarativeMeta'
.
Таким образом, на самом деле нет хорошего способа правильно ввести подсказку declarative_base
. Мой подход состоял бы в том, чтобы просто добавить его в строку документации.
Надеюсь, это поможет будущим читателям! :)
Вот еще немного информации о метаклассах type/:
person
dylanmorroll
schedule
03.06.2021