Доступ к элементам в коллекциях.


145

Допустим, у меня есть следующий код:

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

Есть ли способ получить доступ к элементам пронумерованным способом, например:

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

Ответы:


185

Если это так, OrderedDict()вы можете легко получить доступ к элементам путем индексации, получая кортежи пар (ключ, значение) следующим образом

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

Примечание для Python 3.X

dict.itemsвернет итеративный объект представления dict, а не список. Нам нужно обернуть вызов в список, чтобы сделать индексацию возможной.

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

22
Обратите внимание, что в 3.x itemsметод возвращает объект представления целевого словаря, а не список, и не поддерживает нарезку или индексирование. Так что сначала вам нужно превратить его в список. docs.python.org/3.3/library/stdtypes.html#dict-views
Питер ДеГлоппер, 05

8
Копирование элементов, значений или ключей в списки может быть довольно медленным для больших словарей. Я создал переписанный OrderedDict () с другой внутренней структурой данных для приложений, которым приходится делать это очень часто: github.com/niklasf/indexed.py
Niklas

1
@PeterDeGlopper как мне превратить его в список?
Dejell

1
@Dejel - используйте конструктор:list(d.items())
Питер ДеГлоппер,

9
Если вы получаете доступ только к одному элементу, вы можете избежать накладных расходов на память list(d.items()), используя next(islice(d.items(), 1))to get('bar', 'spam')
Quantum7

24

Вам нужно использовать OrderedDict или вам нужен тип, похожий на карту, который каким-то образом упорядочен с быстрой позиционной индексацией? В последнем случае рассмотрите один из множества сортированных типов словаря Python (который упорядочивает пары ключ-значение на основе порядка сортировки ключей). Некоторые реализации также поддерживают быструю индексацию. Например, sortedcontainers проект имеет SortedDict типа только для этой цели.

>>> 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
Вы также можете использовать SortedDictс ключевой функцией, чтобы избежать сравнений. Как: SortedDict(lambda key: 0, ...). После этого ключи не будут отсортированы, но останутся в стабильном порядке и будут индексироваться.
GrantJ 04

19

Это особый случай, если вам нужна первая запись (или близкая к ней) в OrderedDict без создания списка. (Это было обновлено до 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'

(Когда вы в первый раз говорите «следующий ()», это действительно означает «первый».)

В моем неофициальном тесте next(iter(d.items()))с небольшим OrderedDict лишь немного быстрее, чем items()[0]. С OrderedDict из 10 000 записей он next(iter(d.items()))был примерно в 200 раз быстрее, чем items()[0].

НО, если вы сохраните список items () один раз, а затем будете использовать его часто, это может быть быстрее. Или, если вы несколько раз {создаете итератор items () и переходите через него в нужную позицию}, это может быть медленнее.


10
Python 3 OrderedDicts не имеет iteritems()методы, так что вам нужно будет сделать следующее, чтобы получить первый элемент: next(iter(d.items())).
Натан Осман

В Python 3 d.items()вроде итератора нет, так что iter впереди не поможет? Он все равно вернет полный список :(
asksol

1
Обновление: я был неправ, iter (d.items ()) возвращается odict_iteratorи мне подтвердили на IRC #python, что это не создает копию списка.
asksol

@ Натан Осман, спасибо за толчок. Я наконец-то обновился до Python 3!
SteveWithamDuplicate

14

Значительно эффективнее использовать IndexedOrderedDict из indexedпакета.

Следуя комментарию Никласа, я провел тест для OrderedDict и IndexedOrderedDict с 1000 записями.

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 примерно в 100 раз быстрее индексирует элементы в определенной позиции в этом конкретном случае.


Ницца! К сожалению, пока нет в Анаконде.
Константин

1
@Konstantin Фактическое имя пакета indexed.py . Попробуйте установить indexed.pyвместо indexed.
Sven Haile

9

Эта вики сообщества пытается собрать существующие ответы.

Python 2.7

В Python 2, keys(), values()и items()функции OrderedDictсписков возврата. Используя valuesв качестве примера, самый простой способ:

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

Для больших коллекций, где вам нужен только один индекс, вы можете избежать создания полного списка с использованием версий генератора iterkeys, itervaluesи iteritems:

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

Indexed.py пакет предусматривает IndexedOrderedDict, который предназначен для этого случая использования и будет самым быстрым вариантом.

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

Использование itervalues ​​может быть значительно быстрее для больших словарей с произвольным доступом:

$ 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 имеет те же две основные опции (список и генератор), но методы dict по умолчанию возвращают генераторы.

Метод списка:

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

Генераторный метод:

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

Словари Python 3 на порядок быстрее, чем Python 2, и имеют аналогичное ускорение при использовании генераторов.

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

7

Это новая эра, и словари Python 3.6.1 теперь сохраняют свой порядок. Эта семантика не является явной, потому что для этого потребуется одобрение BDFL. Но Раймонд Хеттингер - следующая лучшая вещь (и смешнее), и он приводит довольно веские доводы в пользу того, что словари будут заказываться на очень долгое время.

Итак, теперь легко создавать фрагменты словаря:

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

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

Примечание. Сохранение порядка вставки словаря теперь официально в Python 3.7 .


1

Если вы имеете дело с заранее известным фиксированным количеством ключей , используйте вместо этого встроенные именованные кортежи Python . Возможный вариант использования - это когда вы хотите сохранить некоторые постоянные данные и обращаться к ним по всей программе, как путем индексации, так и путем указания ключей.

import collections
ordered_keys = ['foo', 'bar']
D = collections.namedtuple('D', ordered_keys)
d = D(foo='python', bar='spam')

Доступ по индексации:

d[0] # result: python
d[1] # result: spam

Доступ по указанию ключей:

d.foo # result: python
d.bar # result: spam

Или лучше:

getattr(d, 'foo') # result: python
getattr(d, 'bar') # result: spam

1

Если вы pandasустановили, вы можете преобразовать заказанный диктант в панд Series. Это позволит произвольный доступ к элементам словаря.

>>> import collections
>>> import pandas as pd
>>> d = collections.OrderedDict()
>>> d['foo'] = 'python'
>>> d['bar'] = 'spam'

>>> s = pd.Series(d)

>>> s['bar']
spam
>>> s.iloc[1]
spam
>>> s.index[1]
bar

0

для OrderedDict () вы можете получить доступ к элементам путем индексации, получая кортежи пар (ключ, значение) следующим образом или используя '.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']
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.