2010-04-16 11 views
6

Estoy tratando de escribir una interfaz gráfica de usuario simple para Plurk usando pyplurk.No se puede iterar sobre una clase de lista en Python

He logrado que cree la conexión API, inicie sesión y recupere y muestre una lista de amigos. Ahora estoy tratando de recuperar y mostrar una lista de Plurks.

pyplurk proporciona una función GetNewPlurks de la siguiente manera:

def GetNewPlurks(self, since): 
    '''Get new plurks since the specified time. 
    Args: 
     since: [datetime.datetime] the timestamp criterion. 
    Returns: 
     A PlurkPostList object or None. 
    ''' 
    offset = jsonizer.conv_datetime(since) 
    status_code, result = self._CallAPI('/Polling/getPlurks', offset=offset) 
    return None if status_code != 200 else \ 
      PlurkPostList(result['plurks'], result['plurk_users'].values()) 

Como se puede ver este devuelve un PlurkPostList, que a su vez se define como sigue:

class PlurkPostList: 
    '''A list of plurks and the set of users that posted them.''' 
    def __init__(self, plurk_json_list, user_json_list=[]): 
    self._plurks = [PlurkPost(p) for p in plurk_json_list] 
    self._users = [PlurkUser(u) for u in user_json_list] 
    def __iter__(self): 
    return self._plurks 
    def GetUsers(self): 
    return self._users 
    def __eq__(self, other): 
    if other.__class__ != PlurkPostList: return False 
    if self._plurks != other._plurks: return False 
    if self._users != other._users: return False 
    return True 

Ahora espera que sea capaz de haz algo como esto:

api = plurk_api_urllib2.PlurkAPI(open('api.key').read().strip(), debug_level=1) 
plurkproxy = PlurkProxy(api, json.loads) 
user = plurkproxy.Login('my_user', 'my_pass') 
ps = plurkproxy.GetNewPlurks(datetime.datetime(2009, 12, 12, 0, 0, 0)) 
print ps 
for p in ps: 
    print str(p) 

Cuando ejecuto esto, lo que realmente obtengo es:

<plurk.PlurkPostList instance at 0x01E8D738> 

de la "ps de impresión", entonces:

for p in ps: 
TypeError: __iter__ returned non-iterator of type 'list' 

No entiendo - sin duda es una lista iterables? ¿Dónde me estoy equivocando? ¿Cómo accedo a Plurks en PlurkPostList?

+0

No relacionado con el problema que está teniendo, siempre heredo 'objeto' en lugar de nada (es decir,' clase PlurkPostList (objeto): ') por lo que estoy usando * nuevas clases de estilo *. –

+0

... y si haces que 'PlurkPostList' herede de la' lista' estándar tienes mucho menos trabajo por hacer. – bobince

+0

@bobince, No realmente. Heredar la lista no te da nada que valga la pena. –

Respuesta

14

Al definir su propio método __iter__, usted debe darse cuenta de que de que __iter__ método debe devolver un iterador , no un iterables. Está devolviendo una lista, no un iterador a una lista, por lo que falla. Puede solucionarlo haciendo return iter(self._plurks), por ejemplo.

Si quería hacer algo un poco más complejo, como el proceso de cada elemento de self._plurks como está siendo repiten a lo largo, el truco habitual es hacer que su método __iter__ ser un generador. De esta manera, el returnvalue de la llamada a __iter__ es el generador, que es un iterador:

def __iter__(self): 
    for item in self._plurks: 
     yield process(item) 
+1

Gracias, esto funciona a la perfección.La definición de la clase PlurkPostList, incluida la función __iter__, provino directamente de PyPlurk, así que asumí alegremente que estaba bien y mi propia vocación fue deficiente. Tampoco había llegado a la distinción entre un iterador y un iterable, por lo que fue muy útil. Gracias. – Vicky

5

__iter__ El método debe devolver un objeto que implementa el método next().

Una lista no tiene un método next(), pero tiene un método __iter__, que devuelve un objeto listiterator. El objeto listiterator tiene un método next().

Usted debe escribir:

def __iter__(self): 
    return iter(self._plurks) 
0

Como alternativa, también se puede definir la función next() y tienen __iter__() de retorno automático. Vea Build a Basic Python Iterator para un buen ejemplo.

+1

Sin embargo, eso haría que las iteraciones múltiples sobre el mismo objeto fueran engorrosas. –

+0

¿Cómo sería engorroso? ¿Me estoy perdiendo de algo? Tenía la impresión de que usar generadores, como lo hizo, logró lo mismo que usar next(). Por favor corrígeme si estoy equivocado. – Jon

+1

Cuando hace que un objeto sea un iterador, debería mantener el estado de la iteración. Tendría que recordar con qué frecuencia se había llamado al método 'next'. Cuando usas un generador, el generador lo hace por ti. Cuando implementa un iterador, necesita almacenar ese estado en algún lugar del iterador. Cuando un objeto es su propio iterador (como, por ejemplo, un "archivo"), el objeto solo se puede iterar una vez y luego tiene que rebobinarse (en el caso de 'archivo', con el método' seek' o volviendo a abrir.) –