2012-03-28 14 views
21

Tengo od del tipo OrderedDict. Quiero acceder a su par agregado más recientemente (clave, valor). od.popitem(last = True) lo haría, pero también eliminaría el par de od que no quiero.Último elemento en OrderedDict

¿Cuál es una buena manera de hacerlo? Puedo/debo hacer esto:

class MyOrderedDict(OrderedDict): 
    def last(self): 
    return next(reversed(self)) 

Respuesta

38

Usando next(reversed(od)) es una manera perfecta de acceder al elemento más recientemente añadido. La clase OrderedDict utiliza una lista doblemente vinculada para los elementos del diccionario e implementa __reversed__(), por lo que esta implementación le otorga O (1) acceso al elemento deseado. Si vale la pena la subclase OrderedDict() para esta operación simple puede ser cuestionada, pero no hay nada realmente malo con este enfoque.

+0

Pero eso solo devuelve la clave del último artículo en el OD, ¿verdad? no el ítem completo (clave, valor). 'next (invertido (OrderedDict ([(0, 'a'), (1, 'b'), (2, 'c')])))' da '2' no' (2, 'c') ' – hobs

+14

@hobs: Sí, solo te da la clave. Cómo obtener el valor dado a la clave se deja como un ejercicio para el lector. :) –

+0

:) así que es O (2) por el problema del OP. detalles que sé ... – hobs

1

Su idea está bien, sin embargo, el iterador predeterminado solo está sobre las teclas, por lo que su ejemplo solo devolverá la última clave. Lo que realmente quiere es:

class MyOrderedDict(OrderedDict): 
    def last(self): 
     return list(self.items())[-1] 

Esto le da al (key, value) pares, no sólo las llaves, como lo hubiera querido.

Tenga en cuenta que en las versiones pre-3.x de Python, OrderedDict.items() devuelve una lista, por lo que no es necesario la llamada list(), pero las versiones posteriores de devolver un dictionary view object, por lo que se quiere.

Editar: Como se ha señalado en los comentarios, la operación más rápida es hacer:

class MyOrderedDict(OrderedDict): 
    def last(self): 
     key = next(reversed(self)) 
     return (key, self[key]) 

Aunque debo admitir que encuentro esto más feo en el código (que nunca ha gustado conseguir la llave a continuación, haciendo x[key] para obtener el valor por separado, prefiero obtener la tupla (key, value)). Dependiendo de la importancia de la velocidad y sus preferencias, es posible que desee elegir la opción anterior.

+2

Esta será una operación O (n), por supuesto. Sería mejor utilizar la implementación original para obtener la clave y obtener el valor con el acceso normal al diccionario. (Y cuando de todos modos tiene que completar una lista, '[-1]' es mucho más fácil que 'next (invertido (...))'.) –

+0

Usted hace un buen punto, corregido. Demasiado fácil encerrarse en una sola forma de resolver un problema. –

9

Un poco de magia de timeit puede ayudar aquí ...

from collections import OrderedDict 
class MyOrderedDict1(OrderedDict): 
    def last(self): 
    k=next(reversed(self)) 
    return (k,self[k]) 

class MyOrderedDict2(OrderedDict): 
    def last(self): 
    out=self.popitem() 
    self[out[0]]=out[1] 
    return out 

class MyOrderedDict3(OrderedDict): 
    def last(self): 
    k=(list(self.keys()))[-1] 
    return (k,self[k]) 

if __name__ == "__main__": 
    from timeit import Timer 

    N=100 

    d1=MyOrderedDict1() 
    for i in range(N): d1[i]=i 

    print ("d1",d1.last()) 

    d2=MyOrderedDict2() 
    for i in range(N): d2[i]=i 

    print ("d2",d2.last()) 

    d3=MyOrderedDict3() 
    for i in range(N): d3[i]=i 

    print("d3",d3.last()) 



    t=Timer("d1.last()",'from __main__ import d1') 
    print ("OrderedDict1",t.timeit()) 
    t=Timer("d2.last()",'from __main__ import d2') 
    print ("OrderedDict2",t.timeit()) 
    t=Timer("d3.last()",'from __main__ import d3') 
    print ("OrderedDict3",t.timeit()) 

resultados en:

d1 (99, 99) 
d2 (99, 99) 
d3 (99, 99) 
OrderedDict1 1.159217119216919 
OrderedDict2 3.3667118549346924 
OrderedDict3 24.030261993408203 

(Probado en python3.2, Ubuntu Linux).

Como señala @SvenMarnach, el método que describió es bastante eficiente en comparación con las otras dos formas en que podría cocinar.

Cuestiones relacionadas