小さな、難読化された型チェッカーを作成しようとしているときに、受け入れられないコード パターンが発見されました。ただし、一貫性がなく、適切に機能しません。これは、最初にテスト用に作成されたコードです。
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)
以下は、Python のインタープリターとの会話を示しており、発生するエラーを強調しています。ATypeError
が生成されますが、予期されたものではありません。ジェネレーターは問題ありませんでしたが、現在は失敗しています。
>>> 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
statictypes
関数を書き直すことができ、関数isallinstance
を再定義してラップすることができます。statictypes
最も簡単な解決策は、ジェネレーターをリスト内包表記に書き直すことです。
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)])))
その後、isallinstance
ゼロから再作成すると、意志は期待どおりに機能し始めます。2 番目の引数のTypeError
何が問題であったかを示す記述は、必要に応じて適切に生成されます。
>>> 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'>
質問:
- ジェネレーターを使用した最初の関数が機能する場合と失敗する場合があるのはなぜですか?
- ジェネレーターがシーケンスと見なされないのはなぜですか (シーケンスを生成するため)?
- ジェネレーターが時々動作するのに、なぜシーケンスが必要なのですか?