У меня есть вспомогательный метод в Python, который возвращает список методов и аннотированные данные для каждого метода. Так что это диктат списков. Аннотированные данные выражаются классом Attribute
.
Определение выглядит следующим образом:
# A filter predicate can be either an attribute object or a tuple/list of attribute objects.
AttributeFilter = Union['Attribute', Iterable['Attribute'], None]
# A class offering a helper method
class Mixin:
def GetMethods(self, filter: AttributeFilter=Attribute) -> Dict[Callable, List[Attribute]]:
pass
Этот синтаксис и соответствующая проверка типов работают нормально.
Причины я хотел бы чтобы улучшить его.
Пользователи обычно получают пользовательские атрибуты из класса Attribute
. Я хотел бы express, если пользователь передает производный класс, такой как UserAttribute
в GetMethods
, то есть возвращает список списков UserAttribute
s.
# Some user-defined attribute and some public data in it
class UserAttribute(Attribute):
someData: str
# Create a big class
class Big(mixin):
# Annotate a method with meta information
@UserAttribute("hello")
def method(self):
pass
# Create an instance
prog = Big()
# search for all methods that have 'UserAttribute' annotations
methods = prog.GetMethods(filter=UserAttribute)
for method, attributes in methods:
for attribute in attributes:
print(attribute.someData)
Этот код может быть выполняется без проблем, но средство проверки типов PyCharm не знает, что поле someData
существует для attribute
в последней строке (вызов печати).
Возможное решение 1: Я мог бы использовать указание типа для каждой переменной, получающей возвращаемое значение из GetMethods
, например:
methods:Dict[Callable, List[UserAttribute]] = prog.GetMethods(filter=UserAttribute)
Этот подход копирует много кода.
Возможное решение 2: Есть можно абстрагировать Dict[Callable, List[UserAttribute]]
в какой-то новый generi c, чтобы я мог использовать:
# pseudo code
UserGeneric[G] := Dict[Callable, List[G]]
# shorter usage
methods:UserGeneric[UserAttribute] = prog.GetMethods(filter=UserAttribute)
Возможное решение 3: В лучшем случае я бы хотел бы использовать TypeVar
вот так:
Attr = TypeVar("Attr", Attribute)
# A filter predicate can be either an attribute object or a tuple/list of attribute objects.
AttributeFilter = Union[Attr, Iterable[Attr], None]
# A class offering a helper method
class Mixin:
def GetMethods(self, filter: AttributeFilter=Attribute) -> Dict[Callable, List[Attr]]:
pass
К сожалению, TypeVar
ожидает как минимум 2 ограничения, таких как T = TypeVar("T", str, byte)
.
В конце концов, это Более сложный вариант простого примера показан на страницах справочника по печатанию:
T = TypeVar("T")
def getElement(l: List[T]) -> T:
pass
Последний вопрос:
Как ограничить TypeVar T для определенных объектов класса и всех его подклассов, без необходимости объединения, как в приведенном выше примере str и byte.
Этот вопрос относится к https://github.com/Paebbels/pyAttributes .