Dostęp do elementów w kolekcji


142

Powiedzmy, że mam następujący kod:

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

Czy istnieje sposób, w jaki mogę uzyskać dostęp do elementów w numerowany sposób, na przykład:

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

Odpowiedzi:


181

Jeśli jest to a OrderedDict(), możesz łatwo uzyskać dostęp do elementów, indeksując je, pobierając krotki par (klucz, wartość) w następujący sposób

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

Uwaga dotycząca języka Python 3.X

dict.itemszwróci raczej iterowalny obiekt widoku dyktowania niż listę. Musimy zawinąć wywołanie do listy, aby umożliwić indeksowanie

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

21
Zwróć uwagę, że w 3.x itemsmetoda zwraca raczej obiekt widoku słownika, a nie listę, i nie obsługuje dzielenia ani indeksowania. Więc najpierw musiałbyś przekształcić go w listę. docs.python.org/3.3/library/stdtypes.html#dict-views
Peter DeGlopper,

8
Kopiowanie elementów, wartości lub kluczy na listy może być dość powolne w przypadku dużych słowników. Stworzyłem przepisanie OrderedDict () z inną wewnętrzną strukturą danych dla aplikacji, które muszą to robić bardzo często: github.com/niklasf/indexed.py
Niklas

1
@PeterDeGlopper jak zamienić to w listę?
Dejell,

1
@Dejel - użyj konstruktora:list(d.items())
Peter DeGlopper

9
Jeśli masz dostęp tylko do jednego przedmiotu, możesz uniknąć narzutu pamięci list(d.items()), używając next(islice(d.items(), 1))polecenia get('bar', 'spam')
Quantum7

24

Czy musisz używać OrderedDict, czy konkretnie chcesz, aby typ podobny do mapy był uporządkowany w jakiś sposób z szybkim indeksowaniem pozycyjnym? Jeśli to drugie, rozważ jeden z wielu posortowanych typów dykt w Pythonie (który porządkuje pary klucz-wartość na podstawie kolejności sortowania kluczy). Niektóre implementacje obsługują również szybkie indeksowanie. Na przykład projekt sortedcontainers ma typ SortedDict tylko do tego celu.

>>> 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'

1
Możesz również użyć SortedDictz kluczową funkcją, aby uniknąć porównań. Jak: SortedDict(lambda key: 0, ...). Klucze zostaną wówczas nieposortowane, ale pozostaną w stabilnej kolejności i będą indeksowane.
GrantJ

19

Oto szczególny przypadek, jeśli chcesz, aby pierwszy wpis (lub blisko niego) w OrderedDict, bez tworzenia listy. (To zostało zaktualizowane do Pythona 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'

(Kiedy pierwszy raz powiesz „następny ()”, tak naprawdę oznacza „pierwszy”).

W moim nieformalnym teście next(iter(d.items()))z małym OrderedDict jest tylko odrobinę szybszy niż items()[0]. Z OrderedDict wynoszącym 10000 wpisów next(iter(d.items()))był około 200 razy szybszy niż items()[0].

ALE jeśli zapiszesz listę items () raz, a następnie będziesz jej często używać, może to być szybsze. Lub jeśli wielokrotnie {tworzysz iterator items () i przechodzisz przez to do żądanej pozycji}, może to być wolniejsze.


10
Python 3 OrderedDicts nie mają iteritems()sposobu, więc trzeba będzie wykonać następujące czynności w celu uzyskania pierwszego elementu: next(iter(d.items())).
Nathan Osman

W Pythonie 3 d.items()nie wydaje się być iteratorem, więc iter z przodu nie pomoże? Nadal zwróci pełną listę :(
asksol

1
Aktualizacja: Myliłem się, iter (d.items ()) powraca odict_iteratori zostało mi potwierdzone na IRC #python, że to nie tworzy kopii listy.
asksol

@Nathan Osman, dzięki za ponaglenie. Niedawno zaktualizowałem się do Pythona 3!
SteveWithamDuplicate

14

Znacznie bardziej wydajne jest użycie IndexedOrderedDict z indexedpakietu.

Idąc za komentarzem Niklasa, przeprowadziłem test porównawczy na OrderedDict i IndexedOrderedDict z 1000 wpisów.

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 jest ~ 100 razy szybsze w indeksowaniu elementów w określonej pozycji w tym konkretnym przypadku.


Miły! Niestety jeszcze nie w Anaconda.
Konstantin

1
@Konstantin Rzeczywista nazwa pakietu to indexed.py . Spróbuj zainstalować indexed.pyzamiast indexed.
Sven Haile,

9

To wiki społeczności próbuje zebrać istniejące odpowiedzi.

Python 2.7

W python 2, keys(), values()oraz items()funkcji OrderedDictlist powrotów. Na valuesprzykład najprostszy sposób to

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

W przypadku dużych zbiorów, w którym tylko zależy na pojedynczym wskaźniku, można uniknąć tworzenia pełną listę używając wersji generatora iterkeys, itervaluesi iteritems:

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

Indexed.py pakiet zapewnia IndexedOrderedDict, który jest przeznaczony dla tego przypadku użycia i będzie najszybszym rozwiązaniem.

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

Korzystanie z itervalues ​​może być znacznie szybsze w przypadku dużych słowników z dostępem swobodnym:

$ 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.0

Python 3 ma te same dwie podstawowe opcje (list vs generator), ale metody dict domyślnie zwracają generatory.

Metoda listy:

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

Metoda generatora:

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

Słowniki Python 3 są o rząd wielkości szybsze niż Python 2 i mają podobne przyspieszenia przy korzystaniu z generatorów.

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

7

Nadeszła nowa era, a słowniki Pythona 3.6.1 zachowują swoją kolejność. Te semantyki nie są wyraźne, ponieważ wymagałoby to zatwierdzenia BDFL. Ale Raymond Hettinger jest następną najlepszą rzeczą (i zabawniejszą) i przedstawia całkiem mocne argumenty, że słowniki będą zamawiane przez bardzo długi czas.

Więc teraz łatwo jest tworzyć wycinki słownika:

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

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

Uwaga: Zachowywanie kolejności reklam Dictonary jest teraz oficjalne w Pythonie 3.7 .


0

dla OrderedDict () można uzyskać dostęp do elementów poprzez indeksowanie, pobierając krotki par (klucz, wartość) w następujący sposób lub używając '.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']
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.