Anahtarları / değerleri beyan edilenle aynı sırada nasıl tutabilirim?


320

Belirli bir düzende beyan ettiğim ve her zaman bu sırada tutmak istediğim bir sözlüğüm var. Anahtarlar / değerler gerçekten değerlerine göre sırayla tutulamıyor, sadece beyan ettiğim sırayla istiyorum.

Yani sözlüğüm varsa:

d = {'ac': 33, 'gw': 20, 'ap': 102, 'za': 321, 'bs': 10}

Ben görüntülemek ya da yineleme bu sırayla değil, Python anahtarları / değerleri ilan ettiğim açık düzeni tutmak emin olmak için herhangi bir yolu var mı?

Yanıtlar:


229

Python 3.6'dan itibaren, standart dicttip varsayılan olarak ekleme sırasını korur.

tanımlanması

d = {'ac':33, 'gw':20, 'ap':102, 'za':321, 'bs':10}

anahtarlar kaynak kodunda listelenen sırada bir sözlükle sonuçlanır.

Bu, seyrek karma tablosu için tamsayılarla basit bir dizi kullanılarak başarıldı; burada bu tamsayılar, anahtar / değer çiftlerini (artı hesaplanan karma) depolayan başka bir diziye endeksler. Bu son dizi, öğeleri ekleme sırasında saklar ve tüm kombinasyon aslında Python 3.5 ve öncesinde kullanılan uygulamadan daha az bellek kullanır. Ayrıntılar için Raymond Hettinger'ın orijinal fikir gönderisine bakın.

3.6'da bu hala bir uygulama detayı olarak görülüyordu; bkz Python 3.6 Neler Yeni belgelerinde :

Bu yeni uygulamanın siparişi koruyan yönü bir uygulama detayı olarak kabul edilir ve buna güvenilmemelidir (bu gelecekte değişebilir, ancak dil spesifikasyonunu değiştirmeden önce bu yeni dict uygulamasının birkaç sürüm için dilde olması arzu edilir. mevcut ve gelecekteki tüm Python uygulamaları için siparişi koruyan semantikleri zorunlu kılmak için; bu aynı zamanda rastgele yineleme sırasının hala geçerli olduğu dilin eski sürümleriyle geriye dönük uyumluluğu korumaya yardımcı olur, örneğin Python 3.5).

Python 3.7, bu uygulama detayını bir dil spesifikasyonuna yükseltir , bu nedenle dictbu sürümle veya daha yeni bir sürümle uyumlu tüm Python uygulamalarında düzeni korumak artık zorunludur . Bkz BDFL tarafından açıklanmasının geri .

collections.OrderedDict()Sınıfı , standart dicttürün üstünde bazı ek işlevler sunduğundan, bazı durumlarda yine de kullanmak isteyebilirsiniz . Tersinir olma ( görünüm nesnelerine uzanır ) ve yeniden sıralamayı destekleme ( move_to_end()yöntemle ) gibi.


Maalesef bu sözlük oluşturma ile ilgili değildir . Koddaki örnekte olduğu gibi belirtilen sözlüklerin aynı sırayı koruyacağı garanti edilmez. Bu, boş bir diksiyon oluşturmanız ve siparişi kontrol etmek istiyorsanız her bir öğeyi manuel olarak eklemeniz gerektiği anlamına gelir.
naught101

8
@ naught101: hayır, bu mu dict oluşturulması için geçerli. Python 3.7 ve sonraki sürümlerde, soruda gösterildiği gibi bir diktü ekranının kullanılması, bu tuşları sırayla listelemeyi garanti eder . Yazılan anahtar / değer çiftleri soldan sağa eklenir, çünkü dil özellikleri şunları garanti eder : Anahtar / veri çiftlerinin virgülle ayrılmış bir sırası verilirse, sözlüğün girişlerini tanımlamak için soldan sağa değerlendirilirler . dict()Belgeleri , hatta bir örnek içerir.
Martijn Pieters

175
from collections import OrderedDict
OrderedDict((word, True) for word in words)

içeren

OrderedDict([('He', True), ('will', True), ('be', True), ('the', True), ('winner', True)])

Değerler True(veya başka bir değiştirilemez nesne) ise, şunları da kullanabilirsiniz:

OrderedDict.fromkeys(words, True)

2
Tabii ki, 'değişmez' kısmın Python'un uygulayacağı zor ve hızlı bir kural olmadığını belirtmek gerekir - onun "sadece" iyi bir fikir.
lvc

11
aşağıdaki gibi çözümlerin OrderedDict(FUTURE=[], TODAY=[], PAST=[])işe yaramayacağına dikkat edin OrderedDict([('FUTURE', []), ('TODAY', []), ('PAST', [])]).
andilabs

2
@andi başka bir sorun var, jsonify kullanırken, OrderedDict json verileri oluştururken siparişi kaybolmuş gibi görünüyor.
tyan

github.com/pallets/flask/issues/974 Bu sorunu çözmek için kullanılabilir ..
tyan

5
Python3.7 artık varsayılan olarak sıralanmıştır. mail.python.org/pipermail/python-dev/2017-Aralık/151283.html
sertsedat

167

Teorik kısmı açıklamak yerine basit bir örnek vereceğim.

>>> from collections import OrderedDict
>>> my_dictionary=OrderedDict()
>>> my_dictionary['foo']=3
>>> my_dictionary['aol']=1
>>> my_dictionary
OrderedDict([('foo', 3), ('aol', 1)])
>>> dict(my_dictionary)
{'foo': 3, 'aol': 1}

16
Dict tipi gibi OrderedDict'i toplu olarak atamanın bir yolu var mı?
tyan

2
OrderedDictgerçekten sorunu çözer, ama ... bu özel örnekte standart bir sözlük kullanarak aynı sonucu alırsınız
Tonechas

2
@Tonechas: Ben sadece standart bir sözlük ile örnek denedim ve var {'aol': 1, 'foo': 3}Bu yüzden iyi bir açıklayıcı örnek var.
twasbrillig

4
Herkes için bir ders var: Python'un öngörülebilir hashinin güvenlik açıklarına yol açabileceği (2.4 sürümünün etrafında olduğunu düşünüyorum) , bu yüzden şimdi aynı kodun iki farklı çalışmasının bile aynı siparişi bir standartta vereceği garantisi yok dict.
holdenweb

1
Arayabileceğin @tyan OrderedDict.update()bir iterable içeren anahtar-değer çiftleri ile: d1.upate([(key1, val1), (key2, val2)]).
Ruud Althuizen

37

Bu yanıtın python3.7'den önceki python sürümleri için geçerli olduğunu unutmayın. CPython 3.6, çoğu durumda uygulama ayrıntısı olarak ekleme talimatını korur. Python3.7'den başlayarak, uygulamaların ekleme talimatının uyumlu olması gerektiği bildirilmiştir.


python sözlükleri sıralanmamış. Sıralı bir sözlük istiyorsanız collections.OrderedDict'i deneyin .

OrderedDict'ın python 2.7'deki standart kütüphaneye eklendiğini unutmayın. Python'un daha eski bir sürümüne sahipseniz, ActiveState'de sipariş edilen sözlükler için tarifler bulabilirsiniz .


yukarıdaki @ martijn'in gönderisine bakın. Python 3.6'dan itibaren, dict ekleme siparişini destekler.
tpk

13

Sözlükler, aramayı verimli hale getiren bir sipariş kullanır ve bunu değiştiremezsiniz,

Sadece bir nesne listesi (basit bir durumda 2 elemanlı bir demet, hatta bir sınıf) kullanabilir ve öğelerin sonuna ekleyebilirsiniz. Daha sonra içindeki öğeleri bulmak için doğrusal aramayı kullanabilirsiniz.

Alternatif olarak, düzeni korumak amacıyla oluşturulan farklı bir veri yapısı oluşturabilir veya kullanabilirsiniz.


Sözlükler, aramayı verimli hale getiren bir sipariş kullanacaktır Son olarak, birisi bunu belirtti.
scharette

7

OrderedDict'ın nasıl çalışacağını anlamaya çalışırken bu yazıyla karşılaştım. Eclipse için PyDev OrderedDict'i bulamadı, bu yüzden sipariş edilmesini istediğim gibi sözlüğümün anahtar değerlerinden bir demet yapmaya karar verdim. Listemi çıkarmam gerektiğinde, sadece tupleın değerleri üzerinden yineledim ve yinelenen 'anahtarı' tupledan sözlüğe, değerleri ihtiyacım olan sırayla almak için sözlüğe ekledim.

misal:

test_dict = dict( val1 = "hi", val2 = "bye", val3 = "huh?", val4 = "what....")
test_tuple = ( 'val1', 'val2', 'val3', 'val4')
for key in test_tuple: print(test_dict[key])

Bu biraz hantal, ama zamana karşı bastırıldım ve bu benim çözümüm.

not: listelerin listesi başka birinin önerdiği yaklaşımın benim için gerçekten bir anlamı yoktur, çünkü listeler sıralanır ve dizine eklenir (ve ayrıca sözlüklerden farklı bir yapıdır).


Harika bir çözüm. Her zaman aynı sırada dosyaya json yazmak için kullanacağım.
Hrvoje T

6

Bir sözlükle gerçekten istediğinizi yapamazsınız. Sözlüğü zaten d = {'ac':33, 'gw':20, 'ap':102, 'za':321, 'bs':10}oluşturdunuz. Zaten oluşturulduktan sonra düzenli kalmanın bir yolu olmadığını buldum. Ne yaptım bunun yerine nesne ile bir json dosyası yapmak oldu:

{"ac":33,"gw":20,"ap":102,"za":321,"bs":10}

Kullandım:

r = json.load(open('file.json'), object_pairs_hook=OrderedDict)

daha sonra kullanıldı:

print json.dumps(r)

doğrulamak için.


1
Öyleyse neden bir listeden bir OrderedDict ile başlamıyorsunuz? JSON dosyası buraya gerçekten bir şey eklemiyor.
Martijn Pieters

Evet, liste düzeni korumak için daha kullanışlıdır, ancak cevap sözlük siparişi ile ilgili soru ile ilgilidir. Sadece bir sözlük kullanmanın sınırlamaları hakkında bilgi vermek ve bir nedenle bir sözlük kullanmaları gerekiyorsa onlara olası bir çözüm bulmak.
nealous3

1
Ama bu bölüm zaten 2012'ye kadar uzanan çok daha eski cevaplarla kaplıdır.
Martijn Pieters

3
from collections import OrderedDict
list1 = ['k1', 'k2']
list2 = ['v1', 'v2']
new_ordered_dict = OrderedDict(zip(list1, list2))
print new_ordered_dict
# OrderedDict([('k1', 'v1'), ('k2', 'v2')])

artık bir zorluğu olmayan ana sorun, bu tuples listesi
Oleg

2

Diğer bir alternatif ise, pandaları dataframedikte benzeri bir yapıdaki öğelerin sırasını ve dizin konumlarını garanti ettiği için kullanmaktır .


1

Genellikle, bir sözlük gibi davranacağını, başta yöntemleri uygulayan olması bir sınıf tasarlayabilir __contains__, __getitem__, __delitem__, __setitem__ve biraz daha. Bu sınıf istediğiniz herhangi bir davranışa sahip olabilir, örneğin sıralı bir yineleyiciyi anahtarlar üzerinden özelleştirmek ...


1

belirli bir sırayla sözlüğe sahip olmak istiyorsanız, ilk öğenin anahtar olacağı ve ikinci öğenin değer olacağı ve bu örneğe benzeyeceği bir liste listesi de oluşturabilirsiniz.

>>> list =[[1,2],[2,3]]
>>> for i in list:
...     print i[0]
...     print i[1]

1
2
2
3

7
Bu bir "sözlük" değildir, çünkü tüm koleksiyonu (O (n) zaman harcayarak) aramadan öğeleri anahtarlarıyla arayamazsınız.
BHSPitMonkey

1
Evet, bir sözlük değildir, ancak duruma bağlı olarak, orijinal posterin sorununa geçerli bir çözüm sağlayabilir.
SunSparc

tam olarak nasıl istediğimizi söylemedi, sadece onları sipariş edebilmek istedim =), her zaman olduğu gibi bir şey yapmanın birçok yolu var.
pelos

1

Bir Django projesi geliştirirken de benzer bir sorun yaşadım. OrderedDict kullanamadım, çünkü python'un eski bir sürümünü çalıştırıyordum, bu yüzden çözüm Django'nun SortedDict sınıfını kullanmaktı:

https://code.djangoproject.com/wiki/SortedDict

Örneğin,

from django.utils.datastructures import SortedDict
d2 = SortedDict()
d2['b'] = 1
d2['a'] = 2
d2['c'] = 3

Not: Bu yanıt ilk olarak 2011'den alınmıştır. Python 2.7 veya daha yüksek bir sürüme erişiminiz varsa, o zaman bu standartta erişebilmeniz gerekir collections.OrderedDict, ki bu konudaki diğerleri tarafından birçok örnek verilmiştir.


0

Sözlük için yaptığımla aynı şeyi yapabilirsiniz.

Liste ve boş bir sözlük oluşturun:

dictionary_items = {}
fields = [['Name', 'Himanshu Kanojiya'], ['email id', 'hima@gmail.com']]
l = fields[0][0]
m = fields[0][1]
n = fields[1][0]
q = fields[1][1]
dictionary_items[l] = m
dictionary_items[n] = q
print dictionary_items
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.