Определенные пользователем типы и коллекции .abc
У меня есть пакет Python, который определяет множество коллекций на основе ABC, предоставляемых collection.abc(Отображение, Последовательность и т.д.). Я хочу воспользоваться преимуществами типа hinting, введенными в Python 3.5, но у меня возникают сомнения относительно того, что будет лучшим способом для этого.
Давайте возьмем один из этих классов в качестве примера; до сих пор у меня было что-то
похожее на это:
from collections.abc import Mapping
class MyMapping(Mapping):
...
Чтобы превратить это в общий тип, документация предлагает сделать что-то вроде этого:
from typing import TypeVar, Hashable, Mapping
K = TypeVar("K", bound=Hashable)
V = TypeVar("V")
class MyMapping(Mapping[K, V]):
...
Но это создает две проблемы:
-
Класс теряет все методы mixin из collection.abc.Mapping. Я мог бы справиться с этим, реализуя их сам, но это в первую очередь превзойдет часть использования ABC.
-
isinstance(MyMapping(), collections.abc.Mapping)
возвращает значение False. Кроме того, при попытке вызвать collections.abc.Mapping.register(MyMapping)
для работы над этим возникает RuntimeError ( "Отказ создать цикл наследования" ).
Моя первая попытка решить эти проблемы состояла в том, чтобы вернуться к расширению коллекций .abc.Mapping:
from typing import TypeVar, Hashable
from collections.abc import Mapping
K = TypeVar("K", bound=Hashable)
V = TypeVar("V")
class MyMapping(Mapping[K, V]):
...
Но это не работает, поскольку collection.abc.Mapping не является общим типом и не поддерживает оператор подписки. Поэтому я попробовал это:
from typing import TypeVar, Hashable, Mapping
from collections.abc import Mapping as MappingABC
K = TypeVar("K", bound=Hashable)
V = TypeVar("V")
class MyMapping(MappingABC, Mapping[K, V]):
...
Но это пахнет рыжим. Импорт и псевдонимы громоздки, в результате класс имеет извилистую MRO, а методы смешения, предоставляемые ABC, не будут печатать информацию...
Итак, какой предпочтительный способ объявить пользовательский общий тип на основе коллекции ABC?
Ответы
Ответ 1
[Personal Opinion ™]: Я действительно не поддерживаю создание новых typing
функций. Они должны быть достаточно обобщенными, чтобы не требовать каких-либо изменений в вашем коде. Если ваш класс сопоставления настолько сложный, он не может быть заменен каким-либо общим отображением (например, dict
), вам лучше просто использовать его:
def foo(bar: MyMapping) -> List:
pass
вместо
def foo(bar: Mapping[K, V]) -> List:
pass
Теперь, если вы хотите, чтобы ваши пользователи могли "набирать" свой класс с помощью typing.Mapping
, вам просто нужно подклассом collections.Mapping
class MyMapping(collections.abc.Mapping):
... # define required methods
isinstance(MyMapping(), typing.Mapping[K, V]) # --> True
Ответ 2
Исходный код отлично работает с текущей версией python и mypy и выполняет все, что вам нужно (включая повторное использование реализации из collections.abc.Mapping
).
Однако пока вы должны удалить bound=Hashable
, так как еще не полностью поддерживается:
from typing import TypeVar, Hashable, Mapping
K = TypeVar("K")
V = TypeVar("V")
class MyMapping(Mapping[K, V]):
...