Как передать аргументы пользовательским подсказкам статического типа в Python 3? - PullRequest
0 голосов
/ 03 сентября 2018

Я большой поклонник и сторонник статических подсказок типов в Python 3. Я использую их некоторое время без проблем.

Я только что натолкнулся на новый крайний случай, который я не могу скомпилировать. Что если я хочу определить пользовательский тип, а затем определить его параметры?

Например, это часто встречается в Python 3:

from typing import List, NewType
CustomObject = NewType('CustomObject', List[int])

def f(data: List[CustomObject]):
    # do something

Но это не скомпилируется:

class MyContainer():
    # some class definition ...

from typing import NewType
SpecialContainer = NewType('SpecialContainer', MyContainer)

def f(data: SpecialContainer[str]):
    # do something

Я понимаю, что SpecialContainer технически функция в этом случае, но она не должна оцениваться как одна в контексте сигнатуры типа. Второй фрагмент кода завершается с TypeError: 'function' object is not subscriptable.

1 Ответ

0 голосов
/ 03 сентября 2018

Образец моего кода

Вы должны разрабатывать свои классы с нуля, чтобы принимать статические подсказки типов. Это не соответствовало моему первоначальному варианту использования, так как я пытался объявить специальные подтипы сторонних классов, но это компилирует мой пример кода.

from typing import Generic, TypeVar, Sequence, List

# Declare your own accepted types for your container, required
T = TypeVar('T', int, str, float)

# The custom container has be designed to accept types hints
class MyContainer(Sequence[T]):
    # some class definition ...

# Now, you can make a special container type
# Note that Sequence is a generic of List, and T is a generic of str, as defined above
SpecialContainer = TypeVar('SpecialContainer', MyContainer[List[str]])

# And this compiles
def f(data: SpecialContainer):
    # do something

Подтип третьего класса

Мое первоначальное намерение состояло в том, чтобы создать подсказку типа, объясняющую, как функция, f(), взяла объект pd.DataFrame, который был проиндексирован целыми числами и все ячейки которого были строками. Используя приведенный выше ответ, я придумал хитрый способ выразить это.

from typing import Mapping, TypeVar, NewType, NamedTuple
from pandas import pd

# Create custom types, required even if redundant
Index = TypeVar('Index')
Row = TypeVar('Row')

# Create a child class of pd.DataFrame that includes a type signature
# Note that Mapping is a generic for a key-value store
class pdDataFrame(pd.DataFrame, Mapping[Index, Row]):
    pass

# Now, this compiles, and explains what my special pd.DataFrame does
pdStringDataFrame = NewType('pdDataFrame', pdDataFrame[int, NamedTuple[str]])

# And this compiles
def f(data: pdStringDataFrame):
    pass

Стоило ли это того?

  • Если вы пишете пользовательский класс, который похож на универсальный контейнер, такой как Sequence, Mapping или Any, то сделайте это. Вы можете добавить переменную типа к определению вашего класса.

  • Если вы пытаетесь записать конкретное использование стороннего класса, который не реализует подсказки типа:

    • Попробуйте использовать существующую переменную типа, чтобы объяснить свою точку зрения, например, MyOrderedDictType = NewType('MyOrderedDictType', Dict[str, float])
    • Если это не сработает, вам придется загромождать свое пространство имен тривиальными дочерними классами и переменными типа, чтобы получить подсказку типа для компиляции. Лучше использовать строку документации или комментарий для объяснения вашей ситуации.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...