Mengakses item dalam koleksi. Diurutkan berdasarkan indeks

142

Katakanlah saya memiliki kode berikut:

import collections
d = collections.OrderedDict()
d['foo'] = 'python'
d['bar'] = 'spam'

Apakah ada cara saya dapat mengakses item dengan cara bernomor, seperti:

d(0) #foo's Output
d(1) #bar's Output
Billjk
sumber

Jawaban:

181

Jika OrderedDict()Anda dapat dengan mudah mengakses elemen dengan mengindeks dengan mendapatkan tupel pasangan (kunci, nilai) sebagai berikut

>>> import collections
>>> d = collections.OrderedDict()
>>> d['foo'] = 'python'
>>> d['bar'] = 'spam'
>>> d.items()
[('foo', 'python'), ('bar', 'spam')]
>>> d.items()[0]
('foo', 'python')
>>> d.items()[1]
('bar', 'spam')

Catatan untuk Python 3.X

dict.itemsakan mengembalikan objek tampilan dict yang dapat diubah daripada daftar. Kita perlu memasukkan panggilan ke daftar untuk memungkinkan pengindeksan

>>> items = list(d.items())
>>> items
[('foo', 'python'), ('bar', 'spam')]
>>> items[0]
('foo', 'python')
>>> items[1]
('bar', 'spam')
Abhijit
sumber
21
Perhatikan bahwa dalam 3.x itemsmetode mengembalikan objek tampilan kamus interable daripada daftar, dan tidak mendukung slicing atau pengindeksan. Jadi, Anda harus mengubahnya menjadi daftar terlebih dahulu. docs.python.org/3.3/library/stdtypes.html#dict-views
Peter DeGlopper
8
Menyalin item, nilai, atau kunci ke dalam daftar bisa sangat lambat untuk kamus besar. Saya membuat penulisan ulang OrderedDict () dengan struktur data internal yang berbeda untuk aplikasi yang harus sering melakukan ini: github.com/niklasf/indexed.py
Niklas
1
@PeterDeGlopper bagaimana cara mengubahnya menjadi daftar?
Dejell
1
@Dejel - gunakan konstruktor:list(d.items())
Peter DeGlopper
9
Jika Anda hanya mengakses satu item, Anda dapat menghindari overhead memori list(d.items())dengan menggunakan next(islice(d.items(), 1))untuk mendapatkan('bar', 'spam')
Quantum7
24

Apakah Anda harus menggunakan OrderedDict atau Anda secara spesifik menginginkan tipe seperti peta yang dipesan dengan cara tertentu dengan pengindeksan posisi cepat? Jika yang terakhir, maka pertimbangkan salah satu dari banyak jenis dict diurutkan Python (yang memesan pasangan nilai kunci berdasarkan urutan kunci). Beberapa implementasi juga mendukung pengindeksan cepat. Misalnya, proyek Sortcontainers memiliki tipe SortedDict hanya untuk tujuan ini.

>>> from sortedcontainers import SortedDict
>>> sd = SortedDict()
>>> sd['foo'] = 'python'
>>> sd['bar'] = 'spam'
>>> print sd.iloc[0] # Note that 'bar' comes before 'foo' in sort order.
'bar'
>>> # If you want the value, then simple do a key lookup:
>>> print sd[sd.iloc[1]]
'python'
GrantJ
sumber
1
Anda juga dapat menggunakan SortedDictdengan fungsi tombol untuk menghindari perbandingan. Seperti: SortedDict(lambda key: 0, ...). Kunci kemudian tidak akan disortir tetapi akan tetap dalam urutan stabil dan dapat diindeks.
GrantJ
19

Ini adalah kasus khusus jika Anda ingin entri pertama (atau dekat dengan itu) di dalam OrderedDict, tanpa membuat daftar. (Ini telah diperbarui ke Python 3):

>>> from collections import OrderedDict
>>> 
>>> d = OrderedDict()
>>> d["foo"] = "one"
>>> d["bar"] = "two"
>>> d["baz"] = "three"
>>> next(iter(d.items()))
('foo', 'one')
>>> next(iter(d.values()))
'one'

(Pertama kali Anda mengatakan "next ()", itu benar-benar berarti "pertama.")

Dalam tes informal saya, next(iter(d.items()))dengan OrderedDict kecil hanya sedikit lebih cepat daripada items()[0]. Dengan OrderedDict dari 10.000 entri, next(iter(d.items()))sekitar 200 kali lebih cepat daripada items()[0].

TETAPI jika Anda menyimpan daftar item () sekali dan kemudian menggunakan banyak daftar, itu bisa lebih cepat. Atau jika Anda berulang kali {membuat item () iterator dan melangkah melaluinya ke posisi yang Anda inginkan}, itu bisa lebih lambat.

SteveWithamDuplikat
sumber
10
Python 3 OrderedDicts tidak memiliki iteritems()metode, sehingga Anda akan perlu melakukan hal berikut untuk mendapatkan item pertama: next(iter(d.items())).
Nathan Osman
Dalam Python 3 d.items()sepertinya tidak menjadi iterator, jadi iter di depan tidak akan membantu? Itu masih akan mengembalikan daftar lengkap :(
Askol
1
Pembaruan: Saya salah, iter (d.items ()) kembali odict_iteratordan dikonfirmasi kepada saya di IRC #python bahwa ini tidak membuat salinan daftar.
Askol
@Nathan Osman, terima kasih atas dorongannya. Saya akhirnya memperbarui diri ke Python 3 baru-baru ini!
SteveWithamDuplicate
14

Secara dramatis lebih efisien menggunakan IndexedOrderedDict dari indexedpaket.

Mengikuti komentar Niklas, saya telah melakukan benchmark pada OrderedDict dan IndexedOrderedDict dengan 1000 entri.

In [1]: from numpy import *
In [2]: from indexed import IndexedOrderedDict
In [3]: id=IndexedOrderedDict(zip(arange(1000),random.random(1000)))
In [4]: timeit id.keys()[56]
1000000 loops, best of 3: 969 ns per loop

In [8]: from collections import OrderedDict
In [9]: od=OrderedDict(zip(arange(1000),random.random(1000)))
In [10]: timeit od.keys()[56]
10000 loops, best of 3: 104 µs per loop

IndexedOrderedDict adalah ~ 100 kali lebih cepat dalam elemen pengindeksan pada posisi tertentu dalam kasus khusus ini.

刘金国
sumber
Bagus! Sayangnya belum di Anaconda.
Konstantin
1
@Konstantin Nama sebenarnya paket ini diindeks.py . Coba instal indexed.pyalih-alih indexed.
Sven Haile
9

Wiki komunitas ini berupaya mengumpulkan jawaban yang ada.

Python 2.7

Dalam python 2, keys(), values(), dan items()fungsi dari OrderedDictdaftar kembali. Menggunakan valuessebagai contoh, cara paling sederhana adalah

d.values()[0]  # "python"
d.values()[1]  # "spam"

Untuk koleksi besar di mana Anda hanya peduli pada satu indeks, Anda dapat menghindari membuat daftar lengkap menggunakan versi generator iterkeys,, itervaluesdan iteritems:

import itertools
next(itertools.islice(d.itervalues(), 0, 1))  # "python"
next(itertools.islice(d.itervalues(), 1, 2))  # "spam"

The indexed.py menyediakan paket IndexedOrderedDict, yang dirancang untuk kasus penggunaan ini dan akan menjadi pilihan tercepat.

from indexed import IndexedOrderedDict
d = IndexedOrderedDict({'foo':'python','bar':'spam'})
d.values()[0]  # "python"
d.values()[1]  # "spam"

Menggunakan itervalues ​​bisa menjadi jauh lebih cepat untuk kamus besar dengan akses acak:

$ python2 -m timeit -s 'from collections import OrderedDict; from random import randint; size = 1000;   d = OrderedDict({i:i for i in range(size)})'  'i = randint(0, size-1); d.values()[i:i+1]'
1000 loops, best of 3: 259 usec per loop
$ python2 -m timeit -s 'from collections import OrderedDict; from random import randint; size = 10000;  d = OrderedDict({i:i for i in range(size)})' 'i = randint(0, size-1); d.values()[i:i+1]'
100 loops, best of 3: 2.3 msec per loop
$ python2 -m timeit -s 'from collections import OrderedDict; from random import randint; size = 100000; d = OrderedDict({i:i for i in range(size)})' 'i = randint(0, size-1); d.values()[i:i+1]'
10 loops, best of 3: 24.5 msec per loop

$ python2 -m timeit -s 'from collections import OrderedDict; from random import randint; size = 1000;   d = OrderedDict({i:i for i in range(size)})' 'i = randint(0, size-1); next(itertools.islice(d.itervalues(), i, i+1))'
10000 loops, best of 3: 118 usec per loop
$ python2 -m timeit -s 'from collections import OrderedDict; from random import randint; size = 10000;  d = OrderedDict({i:i for i in range(size)})' 'i = randint(0, size-1); next(itertools.islice(d.itervalues(), i, i+1))'
1000 loops, best of 3: 1.26 msec per loop
$ python2 -m timeit -s 'from collections import OrderedDict; from random import randint; size = 100000; d = OrderedDict({i:i for i in range(size)})' 'i = randint(0, size-1); next(itertools.islice(d.itervalues(), i, i+1))'
100 loops, best of 3: 10.9 msec per loop

$ python2 -m timeit -s 'from indexed import IndexedOrderedDict; from random import randint; size = 1000;   d = IndexedOrderedDict({i:i for i in range(size)})' 'i = randint(0, size-1); d.values()[i]'
100000 loops, best of 3: 2.19 usec per loop
$ python2 -m timeit -s 'from indexed import IndexedOrderedDict; from random import randint; size = 10000;  d = IndexedOrderedDict({i:i for i in range(size)})' 'i = randint(0, size-1); d.values()[i]'
100000 loops, best of 3: 2.24 usec per loop
$ python2 -m timeit -s 'from indexed import IndexedOrderedDict; from random import randint; size = 100000; d = IndexedOrderedDict({i:i for i in range(size)})' 'i = randint(0, size-1); d.values()[i]'
100000 loops, best of 3: 2.61 usec per loop

+--------+-----------+----------------+---------+
|  size  | list (ms) | generator (ms) | indexed |
+--------+-----------+----------------+---------+
|   1000 | .259      | .118           | .00219  |
|  10000 | 2.3       | 1.26           | .00224  |
| 100000 | 24.5      | 10.9           | .00261  |
+--------+-----------+----------------+---------+

Python 3.6

Python 3 memiliki dua opsi dasar yang sama (list vs generator), tetapi metode dict mengembalikan generator secara default.

Metode daftar:

list(d.values())[0]  # "python"
list(d.values())[1]  # "spam"

Metode generator:

import itertools
next(itertools.islice(d.values(), 0, 1))  # "python"
next(itertools.islice(d.values(), 1, 2))  # "spam"

Kamus Python 3 adalah urutan besarnya lebih cepat dari python 2 dan memiliki speedup serupa untuk menggunakan generator.

+--------+-----------+----------------+---------+
|  size  | list (ms) | generator (ms) | indexed |
+--------+-----------+----------------+---------+
|   1000 | .0316     | .0165          | .00262  |
|  10000 | .288      | .166           | .00294  |
| 100000 | 3.53      | 1.48           | .00332  |
+--------+-----------+----------------+---------+
Quantum7
sumber
7

Ini adalah era baru dan dengan kamus Python 3.6.1 sekarang mempertahankan pesanan mereka. Semantik ini tidak eksplisit karena itu akan membutuhkan persetujuan BDFL. Tapi Raymond Hettinger adalah yang terbaik berikutnya (dan lebih lucu) dan dia membuat kasus yang cukup kuat bahwa kamus akan dipesan untuk waktu yang sangat lama.

Jadi sekarang mudah untuk membuat irisan kamus:

test_dict = {
                'first':  1,
                'second': 2,
                'third':  3,
                'fourth': 4
            }

list(test_dict.items())[:2]

Catatan: Pelestarian urutan penyisipan diktonari sekarang resmi di Python 3.7 .

tiang tinggi
sumber
0

untuk OrderedDict () Anda dapat mengakses elemen dengan mengindeks dengan mendapatkan tupel pasangan (kunci, nilai) sebagai berikut atau menggunakan '.values ​​()'

>>> import collections
>>> d = collections.OrderedDict()
>>> d['foo'] = 'python'
>>> d['bar'] = 'spam'
>>> d.items()
[('foo', 'python'), ('bar', 'spam')]
>>>d.values()
odict_values(['python','spam'])
>>>list(d.values())
['python','spam']
Mehar Rahim
sumber