การเข้าถึงรายการในคอลเลกชันสั่งทำตามดัชนี


142

ให้บอกว่าฉันมีรหัสต่อไปนี้:

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

มีวิธีที่ฉันสามารถเข้าถึงรายการในลักษณะหมายเลขเช่น:

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

คำตอบ:


181

หากOrderedDict()คุณสามารถเข้าถึงองค์ประกอบได้อย่างง่ายดายโดยการสร้างดัชนีโดยรับค่า tuples ของคู่ (คีย์, ค่า) ดังนี้

>>> 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 iterableมากกว่ารายการ เราจำเป็นต้องตัดการเรียกไปยังรายการเพื่อให้สามารถจัดทำดัชนีได้

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

21
โปรดทราบว่าใน 3.x itemsวิธีการจะคืนค่าออบเจ็กต์มุมมองพจนานุกรมที่ทำงานร่วมกันได้แทนที่จะเป็นรายการและไม่สนับสนุนการแบ่งส่วนหรือการทำดัชนี ดังนั้นคุณต้องเปลี่ยนมันเป็นรายการแรก docs.python.org/3.3/library/stdtypes.html#dict-views
Peter DeGlopper

8
การคัดลอกรายการค่าหรือคีย์ลงในรายการอาจค่อนข้างช้าสำหรับ dictonaries ขนาดใหญ่ ฉันสร้าง rewrite of OrderedDict () พร้อม datastructure ภายในที่แตกต่างกันสำหรับแอปพลิเคชันที่ต้องทำสิ่งนี้บ่อยมาก: github.com/niklasf/indexed.py
Niklas

1
@PeterDeGlopper ฉันจะเปลี่ยนเป็นรายการได้อย่างไร
Dejell

1
@Dejel - ใช้ตัวสร้าง:list(d.items())
Peter DeGlopper

9
หากคุณเข้าถึงรายการเดียวคุณสามารถหลีกเลี่ยงค่าใช้จ่ายหน่วยความจำlist(d.items())โดยใช้next(islice(d.items(), 1))เพื่อรับ('bar', 'spam')
Quantum7

24

คุณต้องใช้ OrderedDict หรือคุณต้องการแผนที่ที่มีลักษณะเหมือนที่สั่งในบางวิธีด้วยการจัดทำดัชนีตำแหน่งอย่างรวดเร็วหรือไม่? หากหลังให้พิจารณาหนึ่งในประเภท Dict ที่เรียงลำดับแล้วจำนวนมากของ Python (ซึ่งสั่งคู่คีย์ - ค่าตามลำดับการเรียงลำดับคีย์) การใช้งานบางอย่างยังสนับสนุนการจัดทำดัชนีอย่างรวดเร็ว ตัวอย่างเช่นโครงการSortcontainersมีประเภท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

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]ครั้งเร็วกว่า

แต่ถ้าคุณบันทึกรายการ () รายการหนึ่งครั้งจากนั้นใช้รายการมากนั่นอาจจะเร็วกว่า หรือถ้าคุณซ้ำ {สร้างไอเท็ม () ตัววนซ้ำและก้าวผ่านไปยังตำแหน่งที่คุณต้องการ} นั่นอาจช้าลง


10
งูหลาม 3 OrderedDicts ไม่ได้มีวิธีการเพื่อให้คุณจะต้องทำต่อไปเพื่อให้ได้รายการแรก:iteritems() next(iter(d.items()))
นาธานออสมัน

ใน Python 3 d.items()ดูเหมือนจะไม่เป็นตัววนซ้ำดังนั้นตัววนซ้ำด้านหน้าจะไม่ช่วยอะไร? จะยังคงส่งคืนรายการทั้งหมด :(
Askol

1
อัปเดต: ฉันผิด iter (d.items ()) กลับมาodict_iteratorและได้รับการยืนยันกับฉันใน IRC #python ว่านี่ไม่ได้คัดลอกรายการ
Askol

@ นาธานออสมันขอบคุณสำหรับเขยิบ ในที่สุดฉันก็อัพเดตตัวเองเป็น Python 3 เร็ว ๆ นี้!
SteveWithamDuplicate

14

มีประสิทธิภาพมากขึ้นในการใช้IndexedOrderedDictจากindexedแพคเกจ

ตามความคิดเห็นของ Niklas ฉันได้ทำมาตรฐานในOrderedDictและIndexedOrderedDictด้วย 1,000 รายการ

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 เท่าในองค์ประกอบการจัดทำดัชนีที่ตำแหน่งเฉพาะในกรณีนี้


ดี! ยังไม่ได้อยู่ในอนาคอนด้าโชคไม่ดี
Konstantin

1
@Konstantin ชื่อจริงแพคเกจคือindexed.py ลองติดตั้งindexed.pyindexedแทน
Sven Haile

9

วิกิชุมชนนี้พยายามรวบรวมคำตอบที่มีอยู่

Python 2.7

ในหลาม 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 มีสองตัวเลือกพื้นฐานเหมือนกัน (list vs generator) แต่เมธอด dict จะส่งกลับ generators ตามค่าเริ่มต้น

รายการวิธีการ:

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 และมี speedups ที่คล้ายกันสำหรับการใช้เครื่องกำเนิดไฟฟ้า

+--------+-----------+----------------+---------+
|  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 แต่ Raymond Hettinger เป็นสิ่งที่ดีที่สุดถัดไป (และสนุกกว่า) และเขาทำคดีที่ค่อนข้างแข็งแกร่งที่ที่พจนานุกรมจะได้รับคำสั่งเป็นเวลานานมาก

ดังนั้นตอนนี้มันง่ายที่จะสร้างส่วนของพจนานุกรม:

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

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

หมายเหตุ: dictonary การเก็บรักษาการแทรกการสั่งซื้ออยู่ในขณะนี้อย่างเป็นทางการในหลาม 3.7


0

สำหรับ OrderedDict () คุณสามารถเข้าถึงองค์ประกอบโดยการทำดัชนีโดยรับ tuples ของคู่ (คีย์, ค่า) ดังนี้หรือใช้ '.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']
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.