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.