Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python type hint for classes that support __getitem__

I want to add type hints to a function that will accept any object with a __getitem__ method. For instance, in

def my_function(hasitems, locator):
    hasitems[locator]

I don't want to restrict hasitems to be a specific type like list or dict. As long as it supports __getitem__, it's an appropriate argument to my_function. How can I annotate its type without being unnecessarily restrictive?

Edit: apparently PyCharm can deduce the appropriate hint in a number of common cases, but not in my actual use case. I can't post the code since it's for work, and I haven't been able to find a nonproprietary minimal example where PyCharm fails. In any case, the original question doesn't reference PyCharm and it is still a valid use case for type hints.

like image 550
Dave Kielpinski Avatar asked Mar 12 '19 17:03

Dave Kielpinski


People also ask

What is __ Getitem __ in Python?

__getitem__() is a magic method in Python, which when used in a class, allows its instances to use the [] (indexer) operators. Say x is an instance of this class, then x[i] is roughly equivalent to type(x). __getitem__(x, i) .

What are type hints in Python?

Python's type hints provide you with optional static typing to leverage the best of both static and dynamic typing. Besides the str type, you can use other built-in types such as int , float , bool , and bytes for type hintings. To check the syntax for type hints, you need to use a static type checker tool.

Should I use type hinting in Python?

Type hints work best in modern Pythons. Annotations were introduced in Python 3.0, and it's possible to use type comments in Python 2.7. Still, improvements like variable annotations and postponed evaluation of type hints mean that you'll have a better experience doing type checks using Python 3.6 or even Python 3.7.

Does Python enforce type hints?

The Python runtime does not enforce function and variable type annotations. They can be used by third party tools such as type checkers, IDEs, linters, etc. This module provides runtime support for type hints. The most fundamental support consists of the types Any , Union , Callable , TypeVar , and Generic .


2 Answers

If you're willing to install a not-quite-offical extension to typing, typing-extensions, you can use a Protocol, which should be an implementation of PEP-0544:

from typing_extensions import Protocol
from typing import Any

class GetItem(Protocol):
    def __getitem__(self: 'Getitem', key: Any) -> Any: pass

class BadGetItem:
    def __getitem__(self, a: int, b: int) -> Any: pass

def do_thing(arg: GetItem):
    pass

do_thing(dict())  # OK
do_thing(BadGetItem())  # Fails with explanation of correct signature
do_thing(1)  # Fails
like image 143
Patrick Haugh Avatar answered Oct 01 '22 06:10

Patrick Haugh


It sounds like you essentially want to define your own abstract base class (abc).

Following the documentation above, you can define a custom abc that only dictates the presence of __getitem__, but let's use a predefined one for an example. The Mapping abc consists of __getitem__ and a few other magic methods. You can use abcs in isinstance, but you can also directly use them as a type annotations:

def foo(bar: Mapping):
    pass

Or, using the extended type hinting support of ABCs do even fancies things, which you already saw in other answers:

def foo(bar: Mapping[Any, Any]):
    pass
like image 24
Felk Avatar answered Oct 01 '22 06:10

Felk