2012-07-27 6 views
6

Al intentar escribir un comprobador de tipo diminuto y ofuscado, se descubrió un patrón de código inaceptable. Sin embargo, falla de manera incoherente para funcionar correctamente. Este es el código que fue escrito inicialmente para probarlo.TypeError: function() El argumento después de * debe ser una secuencia, no el generador

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) 

A continuación, se muestra una conversación con el intérprete de Python y se resalta el error que aparece. Se genera un TypeError, pero no el que se esperaba. Mientras que los generadores estaban bien, ahora fallan.

>>> 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 función statictypes puede reescribirse, y la función isallinstance redefinió y envuelto. La solución más simple es reescribir la generatior en statictypes para que sea una lista de comprensión.

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

Después de eso, el isallinstance habrá de empezar a trabajar como se esperaba, una vez que se vuelve a crear desde cero. El TypeError indicando que estaba mal con el segundo argumento se genera correctamente como se deseó.

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

Preguntas:

  1. ¿Por qué la primera función con el generador somtimes trabajo y otras veces fallan?
  2. ¿Por qué un generador no se considera una secuencia (ya que genera una secuencia)?
  3. ¿Por qué se necesita una secuencia cuando un generador obviamente funciona algunas veces?

Respuesta

8
  1. isinstance Porque, al igual que un par de otras funciones de la librería estándar screwy, hace una cosa diferente cuando le das una tupla que otras secuencias. A saber, funciona, y verifica que el tipo sea cualquiera de los dados.
  2. Porque no lo es. Vea el sequence protocol definition. Tendría que implementar __getitem__ para ser uno.
  3. Un error, que todavía hasn't been merged, que le dice que su generador está roto, pero con el mensaje de error incorrecto.

Además, no ensucie nuestro encantador lenguaje con un tipo de verificación como este para cualquier cosa que no sean buenas razones :).

+0

Anuncio 1 .: Observe la línea en la que se produce el error - no hay llamada 'issinstance()' en esa línea. –

+2

Bien, veo - el error se informa en la línea incorrecta, debido al error mencionado en 3. –

+0

He estado trabajando con Python durante aproximadamente 6 años y no necesito verificación de tipo. Este fue solo un experimento para ver qué tan pequeño podría ser un verificador de tipos funcional. Casi nadie parece estar aprovechando las anotaciones de funciones, y esto parecía una forma creativa de usarlas. –

Cuestiones relacionadas