Python type-hinting, indexable object

走远了吗. 提交于 2020-01-13 10:18:31

问题


My function needs to accept an object, from which data can be extracted by index, viz. a List or an instance with defined __getitem__ method.

Which type can I use for type hinting this argument?

Update: As I understand there are presently no such type, I tried to make one myself:

class IndexableContainer(Generic[int, ReturnType]):
    def __getitem__(self, key: int) -> ReturnType:
        ...

But I get the following error:

  File "indexable_container.py", line 22, in IndexableContainer
    class IndexableContainer(Generic[int, ReturnType]):
  File ".../lib/python3.6/typing.py", line 682, in inner
    return func(*args, **kwds)
  File ".../lib/python3.6/typing.py", line 1112, in __getitem__
    "Parameters to Generic[...] must all be type variables")
TypeError: Parameters to Generic[...] must all be type variables

How should I do it?


回答1:


There are several different ways you can do this.

If you're ok with using only custom classes (that you can write) as indexable containers, all you need to do is to adapt your code and remove that 'int' type parameter:

class IndexableContainer(Generic[ReturnType]):
    def __getitem__(self, key: int) -> ReturnType:
        ...

class MyCustomContainer(IndexableContainer[ReturnType]):
    def __getitem__(self, key: int) -> ReturnType:
        # Implementation here

def requires_indexable_container(container: IndexableContainer[ReturnType]) -> ReturnType:
    # Code using container here

The issue is, of course, that if you wanted to pass in a plain old list into the function, you wouldn't be able to do so since list doesn't subclass your custom type.

We could maybe special-case certain inputs via clever use of the @overload decorator and unions, but there's a second, albeit experimental, way of doing this known as Protocols.

Protocols basically let you express "duck typing" in a sane way using type hints: the basic idea is that we can tweak IndexableContainer to become a protocol. Now, any object that implements the __getitem__ method with the appropriate signature is counted as a valid IndexableContainer, whether or not they subclass that type or not.

The only caveat is that Protocols are currently experimental and (afaik) only supported by mypy. The plan is to eventually add protocols to the general Python ecosystem -- see PEP 544 for the specific proposal -- but I haven't kept track of the discussion/don't know what the status of that is.

In any case, to use protocols, install the typing_extensions module using pip. Then, you can do the following:

from typing_extensions import Protocol

# ...snip...


class IndexableContainer(Protocol, Generic[ReturnType]):
    def __getitem__(self, key: int) -> ReturnType:
        ...

def requires_indexable_container_of_str(container: IndexableContainer[str]) -> None:
    print(container[0] + "a")

a = ["a", "b", "c"]
b = {0: "foo", 2: "bar"}
c = "abc"
d = [1, 2, 3]

# Type-checks
requires_indexable_container_of_str(a)
requires_indexable_container_of_str(b)
requires_indexable_container_of_str(c)

# Doesn't type-check
requires_indexable_container_of_str(d)



回答2:


It seems that the closest we can get is:

Mapping[int, Any]

Although it's not quite what I wanted, it's close enough.



来源:https://stackoverflow.com/questions/48283886/python-type-hinting-indexable-object

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!