While trying to write a tiny, obfuscated type checker, an unacceptable code pattern was discovered. However, it inconsistently fails to work properly. This is the code that was initally written to test it with.
def statictypes(a):
def b(a, b, c):
if b in a and not isinstance(c, a[b]): raise TypeError('{} should be {}, not {}'.format(b, a[b], type(c)))
return c
return __import__('functools').wraps(a)(lambda *c: b(a.__annotations__, 'return', a(*(b(a.__annotations__, *d) for d in zip(a.__code__.co_varnames, c)))))
@statictypes
def isallinstance(iterable: object, class_or_type_or_tuple: (type, tuple)) -> bool:
"""isallinstance(iterable, class_or_type_or_tuple) -> bool
Return whether all items in an iterable are instances of a class or of a
subclass thereof. With a type as second argument, return whether that is
all items' type. The form using a tuple, isallinstance(x, (A, B, ...)),
is a shortcut for any(isallinstance(x, y) for y in (A, B, ...)).
"""
return all(isinstance(item, class_or_type_or_tuple) for item in iterable)
The following shows a conversation with Python's interpreter and highlights the error that comes up. A TypeError
is generated, but not the one that was expected. While generators were fine, now they fail.
>>> isallinstance(range(1000000), int)
True
>>> isallinstance(range(1000000), (int, float))
True
>>> isallinstance(range(1000000), [int, float])
Traceback (most recent call last):
File "<pyshell#26>", line 1, in <module>
isallinstance(range(1000000), [int, float])
File "C:\Users\schappell\Downloads\test.py", line 5, in <lambda>
return __import__('functools').wraps(a)(lambda *c: b(a.__annotations__, 'return', a(*(b(a.__annotations__, *d) for d in zip(a.__code__.co_varnames, c)))))
TypeError: isallinstance() argument after * must be a sequence, not generator
The statictypes
function can be rewritten, and the isallinstance
function redefined and wrapped. The simplest solution is to rewrite the generatior in statictypes
to be a list comprehension.
def statictypes(a):
def b(a, b, c):
if b in a and not isinstance(c, a[b]): raise TypeError('{} should be {}, not {}'.format(b, a[b], type(c)))
return c
return __import__('functools').wraps(a)(lambda *c: b(a.__annotations__, 'return', a(*[b(a.__annotations__, *d) for d in zip(a.__code__.co_varnames, c)])))
After that, the isallinstance
will starting working as expected once it is recreated from scratch. The TypeError
stating what was wrong with the second argument is properly generated as was desired.
>>> isallinstance(range(1000000), int)
True
>>> isallinstance(range(1000000), (int, float))
True
>>> isallinstance(range(1000000), [int, float])
Traceback (most recent call last):
File "<pyshell#29>", line 1, in <module>
isallinstance(range(1000000), [int, float])
File "C:\Users\schappell\Downloads\test.py", line 5, in <lambda>
return __import__('functools').wraps(a)(lambda *c: b(a.__annotations__, 'return', a(*[b(a.__annotations__, *d) for d in zip(a.__code__.co_varnames, c)])))
File "C:\Users\schappell\Downloads\test.py", line 5, in <listcomp>
return __import__('functools').wraps(a)(lambda *c: b(a.__annotations__, 'return', a(*[b(a.__annotations__, *d) for d in zip(a.__code__.co_varnames, c)])))
File "C:\Users\schappell\Downloads\test.py", line 3, in b
if b in a and not isinstance(c, a[b]): raise TypeError('{} should be {}, not {}'.format(b, a[b], type(c)))
TypeError: class_or_type_or_tuple should be (<class 'type'>, <class 'tuple'>), not <class 'list'>
Questions:
isinstance
, like a couple of other screwy standard library functions, does a different thing when you give it a tuple than other sequences. Namely, it works, and checks that the type is any of the ones given.__getitem__
to be one.Also, please don't dirty our lovely language with type checking like this for anything but good reasons :).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With