2012-07-27 11 views
6

小さな、難読化されたタイプのチェッカーを作成しようとしているときに、許容できないコードパターンが発見されました。しかし、それは一貫して正しく動作しません。これは最初にそれをテストするために書かれたコードです。TypeError:function()arguments after *は、ジェネレータではなくシーケンスでなければなりません。

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のインタプリタとの会話を示し、そのエラーがハイライト表示されます。 TypeErrorが生成されますが、期待されたものは生成されません。発電機は正常だったが、今は失敗する。

>>> 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のgeneratiorをリストの理解度に書き換えることです。その後

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'> 

質問:

  1. なぜ発電機との最初の関数は、仕事や他の回は失敗somtimesのでしょうか?
  2. なぜジェネレータはシーケンスと見なされないのですか(シーケンスが生成されるため)
  3. ジェネレータが明らかに時間の一部を使っているときにシーケンスが必要なのはなぜですか?

答えて

8
  1. isinstanceので、あなたはそれを他の配列よりもタプルを与えたときに、他の変に標準ライブラリ関数のカップルのように、さまざまなことを行います。すなわち、それは動作し、その型が与えられた型のどれであるかをチェックします。
  2. ではありません。 sequence protocol definitionを参照してください。 __getitem__を1にする必要があります。
  3. まだhasn't been mergedというバグがありますが、ジェネレータが壊れていますが、間違ったエラーメッセージが表示されます。

また、このようなタイプチェックで私たちの素敵な言葉を汚さないでください。

+0

広告1:エラーが発生した行を見てください。その行にはisinstance()コールがありません。 –

+2

OK、私は参照してください - エラーは、3に記載されたバグのために間違った行で報告されています。 –

+0

私はPythonで約6年間働いており、タイプチェックは必要ありません。これは機能的なタイプチェッカーがどれくらい小さく作られたかを見るための実験に過ぎませんでした。関数の注釈を利用している人はほとんどいませんが、これを使用する創造的な方法のようです。 –

関連する問題