Eli Korvigo Eli Korvigo - 3 months ago 16
Python Question

Python: recursive isinstance checking

How can one check a complete type signature of a nested abstract class? In this example

In [4]: from typing import Sequence

In [5]: IntSeq = Sequence[int]

In [6]: isinstance([1], IntSeq)
Out[6]: True

In [7]: isinstance([1.0], IntSeq)
Out[7]: True


I want the last
isinstance
call to actually return
False
, while it only checks that the argument is a
Sequence
. I thought about recursively checking the types, but
IntSeq
has no public attributes that store the nested type(s):

In [8]: dir(IntSeq)
Out[8]:
['__abstractmethods__',
'__class__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__extra__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__le__',
'__len__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__origin__',
'__parameters__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__slots__',
'__str__',
'__subclasshook__',
'__weakref__',
'_abc_cache',
'_abc_negative_cache',
'_abc_negative_cache_version',
'_abc_registry']


So it doesn't seem to be straightforward to get nested types. I can't find relevant information in the docs.

P.S.
I need this for a multiple dispatch implementation.

Update

Thanks to the feedback from Alexander Huszagh and Blender we now know that abstract classes in Python 3.5 (might) have two attributes that store the nested types:
__parameters__
and
__args__
. The former is there under both Linux (Ubuntu) and Darwin (OS X), though it is empty in case of Linux. The later is only available under Linux and stores the types like
__parameters__
does under OS X. This implementation details add up to the confusion.

Jim Jim
Answer

I see you're trying to implement something using a module that is still provisional; you're bound to encounter a changing interface if you do this.

Blender noticed that the __parameters__ argument holds the parameters to the type; this was true until, I believe 3.5.1. In my git clone of the most recent version of Python (3.6.0a4+) __parameters__ again holds an empty tuple, __args__ holds the argument and __origin__ is the first entry in its __bases__ attribute:

>>> intSeq = typing.Sequence[int]
>>> intSeq.__args__
(<class 'int'>,)
>>> intSeq.__parameters__
()
>>> intSeq.__origin__
typing.Sequence<+T_co>

Since 3.6 is when typing will, from what I understand from PEP 411, leave provisional and enter a stable state, this is the version you should be working with to implement your functionality.

Comments