2012-07-27 11 views
6

Durante il tentativo di scrivere un controllo di tipo minuscolo e offuscato, è stato scoperto un pattern di codice inaccettabile. Tuttavia, in modo non coerente non funziona correttamente. Questo è il codice che è stato scritto inizialmente per testarlo.TypeError: L'argomento function() dopo * deve essere una sequenza, non il generatore

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) 

Quanto segue mostra una conversazione con l'interprete di Python e mette in evidenza l'errore che si presenta. Viene generato un numero TypeError, ma non quello previsto. Mentre i generatori stavano bene, ora falliscono.

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

La funzione statictypes può essere riscritta, e la funzione isallinstance ridefinito e avvolto. La soluzione più semplice è riscrivere il generatore in statictypes per essere una lista di comprensione.

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

Dopo di che, il isallinstance sarà iniziare a lavorare come previsto una volta che è ricreato da zero. Il numero TypeError che indica cosa c'è di sbagliato nel secondo argomento viene generato correttamente come desiderato.

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

Domande:

  1. Perché la prima funzione con il generatore somtimes lavoro e altre volte falliscono?
  2. Perché un generatore non è considerato una sequenza (poiché genera una sequenza)?
  3. Perché è necessaria una sequenza quando un generatore ovviamente funziona una volta?

risposta

8
  1. Perché isinstance, come un paio di altre funzioni della libreria standard screwy, fa una cosa diversa quando si dà una tupla di altre sequenze. Vale a dire, funziona e controlla che il tipo sia uno di quelli dati.
  2. Perché non lo è. Vedi lo sequence protocol definition. Dovrebbe essere necessario implementare __getitem__ per essere uno.
  3. Un errore, che è ancora hasn't been merged, che indica che il generatore è guasto, ma con il messaggio di errore errato.

Inoltre, si prega di non sporcare il nostro bel linguaggio con il tipo di controllo come questo per qualsiasi cosa, ma buone ragioni :).

+0

Annuncio 1: guarda la riga in cui si verifica l'errore - non c'è nessuna chiamata 'isinstance()' in quella riga. –

+2

OK, vedo - l'errore è riportato nella riga sbagliata, a causa del bug citato in 3. –

+0

Ho lavorato con Python per circa 6 anni e non ho bisogno di controllo dei tipi. Questo era solo un esperimento per vedere quanto sarebbe stato possibile eseguire un piccolo tipo di controllo funzionale. Quasi nessuno sembra approfittare delle annotazioni delle funzioni, e questo sembrava un modo creativo per usarle. –

Problemi correlati