12

I'm trying to understand how Python's type annotations work (e.g. List and Dict - not list or dict). Specifically I'm interested in how isinstance(list(), List) works, so that I can create my own custom annotations.

I see that List is defined as:

class List(list, MutableSequence[T], extra=list):
    . . .

I'm familiar with metaclass = xxx but I can't find any documentation on this extra = xxx. Is this a keyword or just an argument, and if so, where does it come from and does it do what I'm after? Is it even relevant for isinstance?

Martijn Pieters
  • 889,049
  • 245
  • 3,507
  • 2,997
c z
  • 4,951
  • 3
  • 27
  • 39
  • The fact that it isn't documented either means that a) It isn't part of the public API or b) it was forgotten; I'm leaning towards the first here. Looking at the source for `typing` you can see that `extra` is used to, and I quote: "Construct a `__subclasshook__` callable that incorporates the associated `__extra__` class in subclass checks performed against `cls`." – Dimitris Fasarakis Hilliard May 23 '17 at 11:15
  • 1
    As an aside, your title and question don't *really* match. It seems like your question really involves what `extra`s role is in `isinstance` and not how `isinstance`, in the broad sense, works. – Dimitris Fasarakis Hilliard May 23 '17 at 11:17

1 Answers1

12

The isinstance() and issubclass() have hooks in object.__instancecheck__() and object.__subclasscheck__() that the typing generics also use.

If you want to provide your own generics, you really want to study the typing module source code, specifically how GenericMeta and Generic are used to define the other Generic types like List; mostly such checks are delegated to abc.ABCMeta.__subclasshook__. You can define your own ABC with such a hook, then define a Generic that subclasses it.

It is the GenericMeta metaclass here that also gives the extra keyword argument meaning. Such internals are still sparsely documented because the typing implementation is still in flux, the module is still provisional. The extra argument is stored as __extra__ and is used in a custom __subclasshook__ implementation; for extra=list it simply comes down to translating isinstance(something, List) to isinstance(something, list).

Note that support for run-time checks is deliberately limited; static type checkers will not actually run those hooks. See the structural subtyping discussion in the mypy tracker for further discussion on how the developers are thinking about how to provide better support for complex custom classes that may or may not implement enough methods to be deemed a mapping or a sequence or similar.

Martijn Pieters
  • 889,049
  • 245
  • 3,507
  • 2,997