Bir sözlükteki değerlerle anahtarları nasıl değiştiririm?


101

Giriş olarak bir sözlük alıyorum ve anahtarları girişin değerleri olacak ve değeri karşılık gelen giriş anahtarları olacak bir sözlüğü döndürmek istiyorum. Değerler benzersizdir.

Örneğin, girdimin şöyle olduğunu söyleyin:

a = dict()
a['one']=1
a['two']=2

Çıktımın şöyle olmasını istiyorum:

{1: 'one', 2: 'two'}

Açıklığa kavuşturmak için sonucumun aşağıdakilere eşdeğer olmasını istiyorum:

res = dict()
res[1] = 'one'
res[2] = 'two'

Bunu başarmanın düzgün bir Pythonic yolu var mı?


1
Python 3 kullanıyorsanız güzel bir cevabı olan aynı soru için stackoverflow.com/questions/1087694/… sayfasına bakın
Stephen Edmonds

@Stephen: En çok oylanan ikinci yanıtı görün, bağlantı kurduğunuz soruda kabul edilen yanıtla aynı. Yine de kalabalık diğer yanıtı tercih etti ...
Roee Adler

4
Python perl değildir, python ruby ​​değildir. Okunabilirlik önemlidir. Seyrek yoğun olandan daha iyidir. Bu göz önüne alındığında, bu cevapların tüm yöntemleri sadece kötüdür ™; söz konusu olan, gitmenin en iyi yoludur.
o0 '.

Yanıtlar:


154

Python 2:

res = dict((v,k) for k,v in a.iteritems())

Python 3 (@erik sayesinde):

res = dict((v,k) for k,v in a.items())

15
Bu doğru gibi görünse de, sadece kod yerine nasıl çalıştığına dair bir açıklama eklemek gerçekten iyidir.
Will

4
Ya değerler benzersiz değilse? O zaman anahtarlar bir liste olmalıdır ... örneğin: d = {'a': 3, 'b': 2, 'c': 2} {v: k for k, v in d.iteritems ()} { 2: 'b', 3: 'a'} {2: ['b', 'c'], 3: 'a'} olmalıdır
Hanan Shteingart

4
@HananShteingart: OP'nin sorusu belirtilen değerler benzersizdir. Lütfen vakanız için ayrı bir soru gönderisi oluşturun (ve tercihen diğer kişiler için buraya bağlayın).
liori

python2 kodu işe yarıyor ... ancak liste anlamada [ve ]. Bir liste anlayışı [ve gerektirmez ]mi?
Trevor Boyd Smith

@TrevorBoydSmith: Bu liste anlama değil, bu bir üreteç ifadesidir .
liori

56
new_dict = dict(zip(my_dict.values(), my_dict.keys()))

4
Değerler () ve anahtarların () aynı sıralamaya sahip olması gerçekten garanti ediliyor mu?
Lennart Regebro

1
evet, python.org/dev/peps/pep-3106 adresinden belirtim, öğelerin .keys (), .values ​​() ve .items () tarafından döndürülme sırasının aynı olduğunu (tıpkı Python'da olduğu gibi) ima eder. 2.x), çünkü sıranın tamamı dikt yineleyiciden türetilmiştir (muhtemelen isteğe bağlıdır, ancak bir dikt değiştirilmediği sürece kararlıdır). ancak bu yanıtın my_dict'i iki kez çağırması gerekir (biri değerler için, biri anahtarlar için). belki bu ideal değildir.
sunqiang

4
Evet, bu cevap dikteyi iki kez yineliyor. Sunqiang'ın yanıtı, yalnızca bir yineleme gerektirdiğinden, büyük bir sözlük için tercih edilir.
Carl Meyer

@Carl Meyer: Katılıyorum, ayrıca, büyük veri kümeleri için çok daha iyi olan itertools kullanıyor. Son dict () çağrısının da akış halinde mi yoksa tüm çiftler listesini mi oluşturduğunu merak etsem de
Javier

@CarlMeyer ayrıca n> 1e6 (veya 1e9) için bellek kullanımı da gerçekten büyük olacak ... ve bunu bir grup yavaşlatacak.
Trevor Boyd Smith

47

Python 2.7'den 3.0+ dahil, tartışmasız daha kısa, daha okunabilir bir sürüm var:

>>> my_dict = {'x':1, 'y':2, 'z':3}
>>> {v: k for k, v in my_dict.items()}
{1: 'x', 2: 'y', 3: 'z'}

30
In [1]: my_dict = {'x':1, 'y':2, 'z':3}

In [2]: dict((value, key) for key, value in my_dict.iteritems())
Out[2]: {1: 'x', 2: 'y', 3: 'z'}

3
Orijinal soruda değil, sadece orijinal sözlükte yinelenen değerler varsa ve daha sonra bu yöntemle anahtar / değerler değiştirirseniz ne olacağını merak ediyorum.
Andre Miller

2
@Andre Miller: Belirli anahtarın son oluşumunu alır: dict (((1,3), (1,2))) == {1: 2}
balpha

2
kopyalar, en son karşılaşılan kopya ile üzerine yazılacaktır.
Christopher

2
@Andre Miller: Ve d.items () öğeleri rastgele bir sırayla döndürdüğünden, yinelenen değerler için rastgele bir anahtar elde edersiniz.
Karıncalar Aasma

Bence bulduğu son anahtar, değer çiftini alacaktır. Bu bir ['x'] = 3 gibidir. Sonra bir ['x'] = 4 ayarlarsınız.
riza


18

Deneyebilirsin:

d={'one':1,'two':2}
d2=dict((value,key) for key,value in d.iteritems())
d2
  {'two': 2, 'one': 1}

Bir sözlüğü 'tersine çeviremeyeceğinize' dikkat edin.

  1. Birden fazla anahtar aynı değeri paylaşır. Örneğin {'one':1,'two':1}. Yeni sözlüğün anahtarlı yalnızca bir öğesi olabilir 1.
  2. Değerlerden biri veya daha fazlası karıştırılamaz. Örneğin {'one':[1]}. [1]geçerli bir değerdir, ancak geçerli bir anahtar değildir.

Konuyla ilgili bir tartışma için python posta listesindeki bu konuya bakın .


Ayrıca, orijinal diktedeki değerlerin benzersiz olduğundan emin olma hakkındaki not hakkında +1; aksi takdirde 'tersine çevrilmiş' diktede üzerine yazılır ... ve bu (bunu maliyetime göre buldum) kodunuzda yanıltıcı hatalara neden olabilir!
monojohnny

17

Mevcut ana cevap, değerlerin benzersiz olduğunu varsayar ve bu her zaman böyle değildir. Ya değerler benzersiz değilse? Bilgi kaybedeceksiniz! Örneğin:

d = {'a':3, 'b': 2, 'c': 2} 
{v:k for k,v in d.iteritems()} 

döner {2: 'b', 3: 'a'}.

Hakkındaki bilgiler 'c'tamamen göz ardı edildi. İdeal olarak şöyle bir şey olmalıydı {2: ['b','c'], 3: ['a']}. Bu, alt uygulamanın yaptığı şeydir.

Python 2.x

def reverse_non_unique_mapping(d):
    dinv = {}
    for k, v in d.iteritems():
        if v in dinv:
            dinv[v].append(k)
        else:
            dinv[v] = [k]
    return dinv

Python 3.x

def reverse_non_unique_mapping(d):
    dinv = {}
    for k, v in d.items():
        if v in dinv:
            dinv[v].append(k)
        else:
            dinv[v] = [k]
    return dinv

daha genel bir durumu kapsadığı için bu doğru cevap olmalıdır
Leon Rai

Bunun için teşekkür ederim! Diğer çözümlerle ilgili bilgileri kaybediyordum.

15

res = dict(zip(a.values(), a.keys()))


4
dict, değerlerinin () ve anahtarlarının () öğeleri aynı sırayla döndüreceğini garanti etmez. Ayrıca, anahtarlar (), değerler () ve zip (), yineleyicinin yeterli olacağı bir liste döndürür.
liori

19
@liori: Yanılıyorsun. dict, tabii ki değerler () ve anahtarlar () çağrıları arasındaki dikteyi değiştirmezseniz, değerlerinin () ve anahtarlarının () aynı sırada olacağını garanti eder. Belgelerde şu ifadeler yer almaktadır : ("Not" bölümünü okuyun: docs.python.org/library/stdtypes.html#dict.items ) "Öğeler (), anahtarlar (), değerler (), iteritems (), iterkeys ( ) ve itervalues ​​() sözlüğe müdahale edilmeden çağrılır, listeler doğrudan karşılık gelir. "
nosklo

1
Tamam, öyleyse yanılıyorum ... Çevrimiçi belgeleri kontrol etmedim. Bunu gösterdiğin için teşekkürler.
liori

Bu yanıtı daha verimli hale getirmek için zip yerine yineleyici itertools.izip kullanabilirsiniz.
Alasdair

Ve yinelemeler ve yinelemeler. Ama
iteritems

14
new_dict = dict( (my_dict[k], k) for k in my_dict)

hatta daha iyisi, ancak yalnızca Python 3'te çalışır:

new_dict = { my_dict[k]: k for k in my_dict}

2
Aslında Dict Comprehensions ( PEP 274 ), Python 2.7 ile de çalışır.
Arseny

10

Ilya Prokin'in yanıtını genişletmenin bir başka yolu da reversedişlevi gerçekten kullanmaktır .

dict(map(reversed, my_dict.items()))

Temelde, sözlüğünüz .items()her bir öğenin bir anahtar / değer çifti olduğu (kullanılarak ) yinelenir ve bu öğeler reversedişlevle değiştirilir . Bu kurucuya iletildiğinde dict, onları istediğiniz değer / anahtar çiftlerine dönüştürür.


6

Javier yanıtına yönelik bir iyileştirme önerisi:

dict(zip(d.values(),d))

d.keys()Senin yerine sadece yazabilirsin d, çünkü sözlükten bir yineleyici ile geçersen, ilgili sözlüğün anahtarlarını döndürür.

Örn. bu davranış için:

d = {'a':1,'b':2}
for k in d:
 k
'a'
'b'


2
dict(map(lambda x: x[::-1], YourDict.items()))

.items()bir demet listesi döndürür (key, value). map()listenin öğelerini gözden geçirir ve lambda x:[::-1]onu tersine çevirmek için her bir öğesine (tuple) uygular , böylece her bir demet (value, key), haritadan çıkarılan yeni listede olur . Son olarak, dict()yeni listeden bir dikte yapar.


.items () bir demet listesi (anahtar, değer) döndürür. map () listenin öğelerini gözden geçirir ve lambda x:[::-1]onu tersine çevirmek için her bir öğesine (tuple) uygular , böylece haritadan çıkarılan yeni listede her demet (değer, anahtar) olur. Son olarak, dict () yeni listeden bir dikte yapar.
Ilya Prokin

1

Döngü kullanma : -

newdict = {} #Will contain reversed key:value pairs.

for key, value in zip(my_dict.keys(), my_dict.values()):
    # Operations on key/value can also be performed.
    newdict[value] = key


1

Yerinde bir çözüm ekleme:

>>> d = {1: 'one', 2: 'two', 3: 'three', 4: 'four'}
>>> for k in list(d.keys()):
...     d[d.pop(k)] = k
... 
>>> d
{'two': 2, 'one': 1, 'four': 4, 'three': 3}

Python3 yılında olmanız daha önemlidir list(d.keys())çünkü dict.keysdöner bir görünüm tuşlarının. Python2 kullanıyorsanız d.keys()yeterli.


1

Hanan'ın yanıtı, daha genel bir durumu kapsadığı için doğrudur (diğer yanıtlar, yinelenen durumun farkında olmayan biri için yanıltıcıdır). Hanan'ın cevabındaki bir iyileştirme setdefault kullanıyor:

mydict = {1:a, 2:a, 3:b}   
result = {}
for i in mydict:  
   result.setdefault(mydict[i],[]).append(i)
print(result)
>>> result = {a:[1,2], b:[3]}
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.