2012-07-27 30 views
6

Podczas próby napisania małego, zaciemnionego sprawdzania typu, wykryto niedopuszczalny wzorzec kodu. Jednak niekonsekwentnie nie działa poprawnie. Jest to kod, który został napisany w trybie init, aby go przetestować.TypeError: argument function() po * musi być sekwencją, a nie generatorem

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) 

Poniżej przedstawiono konwersację z interpreterem języka Python i podświetlany błąd. Wygenerowano kod TypeError, ale nie ten, który był oczekiwany. Chociaż generatory były w porządku, teraz zawodzą.

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

Funkcja statictypes mogą być zapisane, a funkcja isallinstance nowo i owijania. Najprostszym rozwiązaniem jest przepisanie generatu w statictypes, aby było zrozumieniem listy.

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)]))) 

Następnie, isallinstance będzie rozpoczęciem pracy zgodnie z oczekiwaniami, gdy jest on ponownie od początku. TypeError stwierdzające, co było nie tak z drugim argumentem, zostało poprawnie wygenerowane zgodnie z oczekiwaniami.

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

Pytania:

  1. Dlaczego pierwsza funkcja z generatorem somtimes pracy, a innym razem nie?
  2. Dlaczego generator nie jest uważany za sekwencję (ponieważ generuje sekwencję)?
  3. Dlaczego sekwencja jest potrzebna, gdy generator działa przez jakiś czas?

Odpowiedz

8
  1. Ponieważ isinstance, podobnie jak kilka innych funkcji bibliotecznych screwy standardowych, robi co innego, gdy dajesz mu krotki niż inne sekwencje. Mianowicie, to działa i sprawdza, czy typ jest jednym z podanych.
  2. Bo tak nie jest. Zobacz sequence protocol definition. Musiałby zaimplementować numer __getitem__.
  3. Błąd, który nadal jest hasn't been merged, który informuje, że generator jest uszkodzony, ale z niepoprawnym komunikatem o błędzie.

Prosimy również, aby nie zabrudzić naszego uroczego języka z typem sprawdzającym w ten sposób, ale z dobrych powodów :).

+0

Ad 1 .: Spójrz na linię, w której wystąpił błąd - nie ma wywołania 'isinstance()' w tej linii. –

+2

OK, widzę - błąd jest zgłaszany w niewłaściwym wierszu, z powodu błędu wymienionego w 3. –

+0

Pracuję z Pythonem przez około 6 lat i nie potrzebuję sprawdzania typu. To był tylko eksperyment, aby zobaczyć, jak mały może być funkcjonalny sprawdzian typu. Prawie nikt nie wydaje się korzystać z adnotacji funkcji, a to wydawało się twórczym sposobem na ich wykorzystanie. –

Powiązane problemy