2012-03-28 43 views
21

Tôi có od loại OrderedDict. Tôi muốn truy cập cặp (khóa, giá trị) được thêm gần đây nhất của nó. od.popitem(last = True) sẽ làm điều đó, nhưng cũng sẽ loại bỏ cặp từ od mà tôi không muốn.Phần tử cuối cùng trong OrderedDict

Cách hay để làm điều đó là gì? Tôi có thể/nên làm điều này:

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

Trả lời

38

Sử dụng next(reversed(od)) là cách hoàn hảo để truy cập phần tử được thêm gần đây nhất. Lớp OrderedDict sử dụng danh sách được liên kết kép cho các mục từ điển và triển khai __reversed__(), vì vậy việc triển khai này cung cấp cho bạn O (1) quyền truy cập vào phần tử mong muốn. Cho dù nó là đáng giá để phân lớp OrderedDict() cho hoạt động đơn giản này có thể được đặt câu hỏi, nhưng không có gì thực sự sai với phương pháp này.

+0

Nhưng điều đó chỉ trả về khóa của mục cuối cùng trong OD, phải không? không phải toàn bộ mục (khóa, giá trị). 'next (đảo ngược (OrderedDict ([(0, 'a'), (1, 'b'), (2, 'c')])))' cho '2' không' (2, 'c') ' – hobs

+14

@hobs: Có, nó chỉ cung cấp cho bạn chìa khóa. Làm thế nào để có được giá trị cho chìa khóa còn lại như là một bài tập cho người đọc. :) –

+0

:) vì vậy nó là O (2) cho vấn đề của OP. chi tiết Tôi biết ... – hobs

1

Ý tưởng của bạn là tốt, tuy nhiên trình lặp lặp mặc định chỉ nằm trên các khóa, vì vậy ví dụ của bạn sẽ chỉ trả lại khóa cuối cùng. Những gì bạn thực sự muốn là:

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

Điều này cung cấp cho các cặp (key, value), không chỉ các phím, như bạn muốn.

Lưu ý rằng trên các phiên bản trước bằng 3.x của Python, OrderedDict.items() trả về một danh sách, vì vậy bạn không cần cuộc gọi list(), nhưng các phiên bản sau sẽ trả lại dictionary view object, vì vậy bạn sẽ làm như vậy.

Edit: Như đã đề cập trong các ý kiến, hoạt động nhanh hơn là để làm:

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

Mặc dù tôi phải thừa nhận tôi tìm thấy điều này xấu xí trong mã (Tôi chưa bao giờ thích nhận được chìa khóa sau đó làm x[key] để có được giá trị một cách riêng biệt, tôi thích nhận được (key, value) tuple) - tùy thuộc vào tầm quan trọng của tốc độ và sở thích của bạn, bạn có thể chọn tùy chọn cũ.

+2

Đây sẽ là một hoạt động O (n), tất nhiên. Nó sẽ là tốt hơn để sử dụng thực hiện ban đầu để có được chìa khóa, và nhận được giá trị bằng cách truy cập từ điển thông thường. (Và khi bạn vẫn phải buld một danh sách, '[-1]' là dễ dàng hơn nhiều hơn 'tiếp theo (đảo ngược (...))'.) –

+0

Bạn làm cho một điểm tốt, sửa chữa. Quá dễ dàng để bị khóa vào một cách để giải quyết vấn đề. –

9

Một kỳ diệu ít từ timeit có thể giúp đây ...

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

kết quả trong:

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

(Thử nghiệm trên python3.2, Ubuntu Linux).

Như được chỉ ra bởi @SvenMarnach, phương pháp bạn mô tả khá hiệu quả so với hai cách khác tôi có thể nấu ăn.

Các vấn đề liên quan