2012-07-27 26 views
6

在嘗試編寫一個很小的混淆類型檢查器時,發現了一個不可接受的代碼模式。但是,它不一致地正常工作。這是最初編寫的用於測試的代碼。TypeError:*之後的函數()參數必須是一個序列,而不是生成器

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中的生成器重寫爲列表理解。

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將開始工作。 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__是一個。
  3. 一個錯誤,仍然hasn't been merged,這是告訴你,你的發電機壞了,但與不正確的錯誤信息。

此外,請不要玷污我們可愛的語言,類型檢查像這樣的任何事情,但很好的理由:)。

+0

廣告1:看看錯誤發生在哪一行 - 在該行中沒有'isinstance()'調用。 – 2012-07-27 14:34:15

+2

好的,我看到了 - 由於在3中提到的錯誤,錯誤報告在錯誤的行中。 – 2012-07-27 14:37:58

+0

我一直在使用Python大約6年,並且不需要類型檢查。這只是一個實驗,可以看出功能類型檢查器可以做多少。幾乎沒有人似乎利用功能註釋,這似乎是一個創造性的方式來使用它們。 – 2012-07-27 14:38:19

相關問題