2011-06-20 10 views
94

En Python, ¿cómo puedo verificar si un objeto es un generador?¿Cómo comprobar si un objeto es un objeto generador en python?

Tratando este -

>>> type(myobject, generator) 

da el error -

Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
NameError: name 'generator' is not defined 

(Sé que puedo comprobar si el objeto tiene un método next para que sea un generador, pero quiero de alguna manera con el cual puedo determinar el tipo de cualquier objeto, no solo los generadores).

+3

¿Qué problema real estás tratando de resolver? Publicar más contexto, puede haber una manera más inteligente. ¿Por qué necesitas saber si es un generador? – Daenyth

+4

'from types import GeneratorType; type (myobject, GeneratorType)' le dará el resultado correcto para los objetos de la clase 'generador'. Pero como Daenyth implica, ese no es necesariamente el camino correcto a seguir. – JAB

+6

Si está buscando '__next__', en realidad está aceptando cualquier iterador, no solo generadores, lo cual es muy probable que desee. – delnan

Respuesta

138

Puede utilizar GeneratorType de tipos:

>>> import types 
>>> types.GeneratorType 
<class 'generator'> 
>>> gen = (i for i in range(10)) 
>>> isinstance(gen, types.GeneratorType) 
True 
23

¿Se refiere a las funciones del generador? use inspect.isgeneratorfunction.

EDIT:

si quieres un generador objeto puede utilizar inspect.isgenerator como ha señalado JAB en su comentario.

+1

la función del generador no es un objeto generador; ver la respuesta de @ utdemir –

+2

@Piotr: En ese caso, usa ['inspect.isgenerator'] (http://docs.python.org/library/inspect.html#inspect.isgenerator). – JAB

+0

@JAB, @Piotr: reflejado para abordar todas las posibilidades de lo que puede significar el OP, gracias JAB :) – mouad

2

Sé que puedo comprobar si el objeto tiene un método siguiente para que sea un generador, pero quiero de alguna manera con el que puedo determinar la tipo de cualquier objeto, no solo generadores.

No haga esto. Es simplemente una idea muy, muy mala.

su lugar, hacer esto:

try: 
    # Attempt to see if you have an iterable object. 
    for i in some_thing_which_may_be_a_generator: 
     # The real work on `i` 
except TypeError: 
    # some_thing_which_may_be_a_generator isn't actually a generator 
    # do something else 

En el caso improbable de que el cuerpo del bucle para también tiene TypeError s, hay varias opciones: (1) definir una función para limitar el alcance de los errores, o (2) usan un bloque anidado try.

O (3) algo así para distinguir todos estos TypeError s que están flotando alrededor.

try: 
    # Attempt to see if you have an iterable object. 
    # In the case of a generator or iterator iter simply 
    # returns the value it was passed. 
    iterator = iter(some_thing_which_may_be_a_generator) 
except TypeError: 
    # some_thing_which_may_be_a_generator isn't actually a generator 
    # do something else 
else: 
    for i in iterator: 
     # the real work on `i` 

O (4) arregle las otras partes de la aplicación para proporcionar los generadores adecuadamente. Eso es a menudo más simple que todo esto.

+1

Tu solución detectará TypeErrors lanzados por el cuerpo del bucle for. He propuesto una edición que evitaría este comportamiento indeseable. – Dunes

+0

Esta es la forma más pitonica de hacerlo, si no me equivoco. – JAB

+0

Aunque, si está iterando sobre una lista de elementos y muchos de ellos no son iteradores que iteradores, ¿podría llevar más tiempo? –

2
>>> import inspect 
>>> 
>>> def foo(): 
... yield 'foo' 
... 
>>> print inspect.isgeneratorfunction(foo) 
True 
+0

Esto funciona solo si es una función. Si 'foo' es un objeto generador, muestra 'False'. Vea mi pregunta, quiero hacer verificaciones de objetos generadores. –

+1

inspect.isgenerator –

9

La función inspect.isgenerator está bien si desea comprobar los generadores puros (es decir, objetos de la clase "generador"). Sin embargo, devolverá False si comprueba, por ejemplo, un izip iterable.Una forma alternativa para el control de un generador generalizado es el uso de esta función:

def isgenerator(iterable): 
    return hasattr(iterable,'__iter__') and not hasattr(iterable,'__len__') 
+1

Hmm. Esto devuelve verdadero para 'x = iter ([1,2])'. Me parece que realmente está probando si un objeto es un ** iterador **, no un generador. Pero tal vez "iterador" es exactamente lo que quiere decir con "generador generalizado". –

10

Creo que es importante hacer distinción entre funciones del generador y generadores (resultado de la función de generador):

>>> def generator_function(): 
...  yield 1 
...  yield 2 
... 
>>> import inspect 
>>> inspect.isgeneratorfunction(generator_function) 
True 

llamada generador_función no dará resultado normal, incluso no ejecutará ningún código en la función en sí, el resultado será un objeto especial llamado generator:

>>> generator = generator_function() 
>>> generator 
<generator object generator_function at 0x10b3f2b90> 

por lo que no es función de generador, pero generador:

>>> inspect.isgeneratorfunction(generator) 
False 

>>> import types 
>>> isinstance(generator, types.GeneratorType) 
True 

y la función del generador no es generador:

>>> isinstance(generator_function, types.GeneratorType) 
False 

sólo para referencia, llamada real del cuerpo de la función va a suceder mediante el consumo de generador, por ejemplo:

>>> list(generator) 
[1, 2] 

Véase también In python is there a way to check if a function is a "generator function" before calling it?

1

Si está utilizando tornado webserver o similar, puede haber encontrado que los métodos de servidor son en realidad generadores y no métodos. Esto hace que sea difícil llamar a otros métodos porque el rendimiento no está funcionando dentro del método y, por lo tanto, debe comenzar a administrar grupos de objetos de generador encadenados. Un método simple para gestionar las piscinas de generadores encadenados es crear una función de ayuda tales como

def chainPool(*arg): 
    for f in arg: 
     if(hasattr(f,"__iter__")): 
      for e in f: 
      yield e 
     else: 
     yield f 

Ahora escritura generadores encadenados como

[x for x in chainPool(chainPool(1,2),3,4,chainPool(5,chainPool(6)))] 

produce una salida

[1, 2, 3, 4, 5, 6] 

que es probablemente lo quieres si buscas utilizar generadores como un hilo alternativo o similar.

Cuestiones relacionadas