Sözlük anahtarını yeniden adlandırma


400

Değerini yeni bir ada atamadan ve eski ad anahtarını kaldırmadan sözlük anahtarını yeniden adlandırmanın bir yolu var mı; ve dict anahtar / değer yoluyla yineleme olmadan?

OrderedDict durumunda, anahtarın konumunu korurken aynısını yapın.


7
"değerini yeni bir ada atamadan ve eski ad anahtarını kaldırmadan" tam olarak ne demek istiyorsun? benim gördüğüm şekilde, bir anahtarı yeniden adlandırmanın tanımı ve aşağıdaki tüm cevaplar değeri yeniden atar ve eski anahtar adını kaldırır. henüz bir cevabı kabul etmediniz, belki bunlar aradığınız şeyi başaramadı mı?
dbliss

5
Gerçekten sürüm numaralarını belirtmeniz gerekir. Python 3.7'den itibaren dil spesifikasyonu, talimatların ekleme talimatını takip etmesini garanti ediyor . (A sürece) listelenen konular hakkında 2.x veya 3.6- veya b) Umrunda da backports bu kodu istediğiniz Bu da OrderedDict çoğunlukla eski yapar Will OrderedDict Python 3.7 de gereksiz hale? ). Ve 3.6'da, sözlük ekleme sırası CPython uygulaması tarafından garanti edildi (ancak dil spesifikasyonu değil).
smci

1
@smci Python'da 3.7 ekleme talimatını takip eder, ancak hala farklıdır OrderedDict. kararlar eşitlik için karşılaştırılırken düzeni görmezden gelirken, karşılaştırılırken OrderedDictdüzeni dikkate alır. Bunu açıklayan bir şeyle bağlantılı olduğunuzu biliyorum, ancak yorumunuzun bu bağlantıyı okumamış olanları yanlış yönlendirmiş olabileceğini düşündüm.
Flimm

@Flimm: bu doğru ama önemli olduğunu göremiyorum, bu soru bir arada iki soru soruyor ve ikinci bölümün amacı geçersiz kılındı. "emirler eşitlik için karşılaştırıldıklarında düzeni görmezden gelirler ." " OrderedDictkarşılaştırılırken düzeni dikkate alır" Tamam ama kimse 3.7 sonrası umurunda değil. Büyük OrderedDictölçüde eski olduğunu iddia ediyorum , neden olmasın nedenlerini açıklayabilir misiniz? örneğin ihtiyacınız olmadığı sürece burada ikna edici değilreversed()
smci

@Flimm: 3.7 ya da 2.x ile uyumlu olması gerekmediği sürece OrderedDict'in 3.7+ sürümünde neden kullanılmadığına dair güvenilir bir argüman bulamıyorum. Yani , bu hiç de ikna edici değil. Özellikle "bir OrderedDict kullanmak niyetinizi iletir ..." tamamen gerekli teknik borç ve gizleme için dehşet verici bir argüman. İnsanlar sadece dikteye geri dönmeli ve onunla başa çıkmalı. Bu kadar basit.
smci

Yanıtlar:


712

Düzenli bir karar için şunları kullanabilirsiniz:

mydict[new_key] = mydict.pop(old_key)

Bir OrderedDict için, bir anlayış kullanarak tamamen yeni bir tane oluşturmanız gerektiğini düşünüyorum.

>>> OrderedDict(zip('123', 'abc'))
OrderedDict([('1', 'a'), ('2', 'b'), ('3', 'c')])
>>> oldkey, newkey = '2', 'potato'
>>> OrderedDict((newkey if k == oldkey else k, v) for k, v in _.viewitems())
OrderedDict([('1', 'a'), ('potato', 'b'), ('3', 'c')])

Bu sorunun sorulduğu gibi, anahtarın kendisinin değiştirilmesi pratik değildir çünkü dikt anahtarları genellikle sayılar, dizeler veya tuples gibi değişmez nesnelerdir. Anahtarı değiştirmeye çalışmak yerine, değeri yeni bir anahtara yeniden atamak ve eski anahtarı kaldırmak, python'da "yeniden adlandırma" yı nasıl elde edebileceğinizdir.


Python 3.5 dict.popiçin, testime dayalı bir OrderedDict için uygulanabilir olduğunu düşünüyorum .
Daniel

5
Hayır, OrderedDictekleme sorusunu koruyacak, bu da sorunun sorduğu şey değil.
wim

64

1 satırda en iyi yöntem:

>>> d = {'test':[0,1,2]}
>>> d['test2'] = d.pop('test')
>>> d
{'test2': [0, 1, 2]}

3
ya sahipsen d = {('test', 'foo'):[0,1,2]}?
Petr Fedosov

4
@PetrFedosov, o zaman yaparsınd[('new', 'key')] = d.pop(('test', 'foo'))
Robert

17
Bu cevap, wim'in aynı şeyi öneren cevabından iki yıl sonra ek bilgi olmadan geldi. Neyi kaçırıyorum?
Andras Deak

@DandDict () ve OrderedDict ()
Tcll

4
wim'in 2013'teki ilk revizyonunda cevabı , o zamandan beri sadece eklemeler yapıldı. Düzenlilik sadece OP'nin kriterinden gelir.
Andras Deak

29

Bir çek kullanarak newkey!=oldkey, bu şekilde şunları yapabilirsiniz:

if newkey!=oldkey:  
    dictionary[newkey] = dictionary[oldkey]
    del dictionary[oldkey]

4
bu güzel çalışıyor, ancak yeni anahtar varsayılan olarak (python3'te) sonunda eklendiği için orijinal siparişi korumuyor.
szeitlin

2
@szeitlin dict, python3.6 + sipariş formunda intialize etse bile , siparişe güvenmemelisiniz , önceki sürümler yapmaz ve gerçekten py3.6 + dicts'in nasıl değiştiğinin bir özelliği değildir. OrderedDictSiparişi önemsiyorsanız kullanın .
Granitosaurus

2
Teşekkürler @Granitosaurus, bunu bana açıklamana gerek yoktu. Yorumumun konusu bu değildi.
szeitlin

5
Bu gerçeği tamamen alakasız yani yorumunuz bazı şekilde, şekil veya gerçekte hiç kimse Sözlük siparişte güvenmelisiniz şeklinde dict sipariş meseleleri değiştirir gerçeği ima @szeitlin
Granitosaurus

11

Tüm sözlük anahtarlarını yeniden adlandırmanız durumunda:

target_dict = {'k1':'v1', 'k2':'v2', 'k3':'v3'}
new_keys = ['k4','k5','k6']

for key,n_key in zip(target_dict.keys(), new_keys):
    target_dict[n_key] = target_dict.pop(key)

1
Tanımdakiyle target_dict.keys()aynı düzende mi garanti ediliyor? Ben bir Python dict sırasız olduğunu düşündüm ve bir
diktenin

Anahtarlarınızı bir noktada sıralamanız gerektiğini düşünüyorum ...
Espoir Murhabazi

10

Bunu OrderedDict recipeRaymond Hettinger tarafından yazılmış ve bir renameyöntem eklemek için değiştirebilirsiniz , ancak bu karmaşık bir O (N) olacaktır:

def rename(self,key,new_key):
    ind = self._keys.index(key)  #get the index of old key, O(N) operation
    self._keys[ind] = new_key    #replace old key with new key in self._keys
    self[new_key] = self[key]    #add the new key, this is added at the end of self._keys
    self._keys.pop(-1)           #pop the last item in self._keys

Misal:

dic = OrderedDict((("a",1),("b",2),("c",3)))
print dic
dic.rename("a","foo")
dic.rename("b","bar")
dic["d"] = 5
dic.rename("d","spam")
for k,v in  dic.items():
    print k,v

çıktı:

OrderedDict({'a': 1, 'b': 2, 'c': 3})
foo 1
bar 2
c 3
spam 5

1
@Merose Python dosyasını modül arama yolunuzda bir yere ekleyin ve OrderedDictburadan içe aktarın . Ama ben tavsiye ederim: Miras alan bir sınıf oluşturun OrderedDictve renameona bir yöntem ekleyin .
Ashwini Chaudhary

OrderedDict o zamandan beri çift bağlantılı bir liste kullanmak için yeniden yazıldı gibi görünüyor, bu yüzden muhtemelen bunu yapmanın bir yolu var, ancak çok daha fazla akrobasi gerektiriyor. : - /
szeitlin

6

Benden birkaç kişi, bir .popastarda bir anahtar silme ve oluşturma hilesinden bahsetti .

Şahsen daha açık uygulamayı daha okunabilir buluyorum:

d = {'a': 1, 'b': 2}
v = d['b']
del d['b']
d['c'] = v

Yukarıdaki kod geri döner {'a': 1, 'c': 2}


1

Diğer cevaplar oldukça iyi.Ancak python3.6'da düzenli dict de sipariş var. Bu nedenle, anahtarın konumunu normal durumda tutmak zordur.

def rename(old_dict,old_name,new_name):
    new_dict = {}
    for key,value in zip(old_dict.keys(),old_dict.values()):
        new_key = key if key != old_name else new_name
        new_dict[new_key] = old_dict[key]
    return new_dict

1

Python 3.6'da (ileriye doğru?) Aşağıdaki tek astarı tercih ederim

test = {'a': 1, 'old': 2, 'c': 3}
old_k = 'old'
new_k = 'new'
new_v = 4  # optional

print(dict((new_k, new_v) if k == old_k else (k, v) for k, v in test.items()))

hangi üretir

{'a': 1, 'new': 4, 'c': 3}

printİpython konsolu / jupyter dizüstü bilgisayarın ifadesi olmadan sözlüğü seçtikleri sırada sunduğunu belirtmek gerekir ...


0

Orijinal sözlüğü değiştirmeyen bu işlevle geldim. Bu işlev aynı zamanda sözlüklerin listesini de destekler.

import functools
from typing import Union, Dict, List


def rename_dict_keys(
    data: Union[Dict, List[Dict]], old_key: str, new_key: str
):
    """
    This function renames dictionary keys

    :param data:
    :param old_key:
    :param new_key:
    :return: Union[Dict, List[Dict]]
    """
    if isinstance(data, dict):
        res = {k: v for k, v in data.items() if k != old_key}
        try:
            res[new_key] = data[old_key]
        except KeyError:
            raise KeyError(
                "cannot rename key as old key '%s' is not present in data"
                % old_key
            )
        return res
    elif isinstance(data, list):
        return list(
            map(
                functools.partial(
                    rename_dict_keys, old_key=old_key, new_key=new_key
                ),
                data,
            )
        )
    raise ValueError("expected type List[Dict] or Dict got '%s' for data" % type(data))

-1

Anahtarları yeniden adlandırırken dict.pop () ile yukarıdaki @wim'in cevabını kullanıyorum, ama bir gotcha buldum. Eski anahtarların listesini dikte örneğinden tamamen ayırmadan anahtarları değiştirmek için dikte dolaşmak, yeni, değiştirilmiş anahtarları döngüye dönüştürmek ve bazı mevcut anahtarları kaçırmak.

Başlamak için şu şekilde yaptım:

for current_key in my_dict:
    new_key = current_key.replace(':','_')
    fixed_metadata[new_key] = fixed_metadata.pop(current_key)

Bu şekilde dikte boyunca bisiklete binmenin, sözlük değiştirmemesi gereken zamanlarda bile anahtarları bulmaya devam ettiğini, yani yeni anahtarları, değiştirdiklerimi buldum! (A) for döngüsünde kendi değiştirilmiş anahtarlarımı bulmaktan kaçınmak ve (b) bir nedenle döngü içinde bulunmayan bazı anahtarları bulmak için örnekleri birbirinden tamamen ayırmak gerekiyordu.

Bunu şimdi yapıyorum:

current_keys = list(my_dict.keys())
for current_key in current_keys:
    and so on...

Değişen dikte referansından kurtulmak için my_dict.keys () öğesini bir listeye dönüştürmek gerekiyordu. Sadece my_dict.keys () kullanmak beni garip yan etkilerle, orijinal örneğe bağlı tuttu.


-1

Birisinin yeni anahtarları içeren bir liste sağlayarak tüm anahtarları aynı anda yeniden adlandırmak istemesi durumunda:

def rename_keys(dict_, new_keys):
    """
     new_keys: type List(), must match length of dict_
    """

    # dict_ = {oldK: value}
    # d1={oldK:newK,} maps old keys to the new ones:  
    d1 = dict( zip( list(dict_.keys()), new_keys) )

          # d1{oldK} == new_key 
    return {d1[oldK]: value for oldK, value in dict_.items()}

-1

@ helloswift123 Ben senin fonksiyonunu seviyorum. Tek bir çağrıda birden fazla anahtarı yeniden adlandırmak için bir değişiklik:

def rename(d, keymap):
    """
    :param d: old dict
    :type d: dict
    :param keymap: [{:keys from-keys :values to-keys} keymap]
    :returns: new dict
    :rtype: dict
    """
    new_dict = {}
    for key, value in zip(d.keys(), d.values()):
        new_key = keymap.get(key, key)
        new_dict[new_key] = d[key]
    return new_dict

-2

Anahtar k3'ü k4 olarak yeniden adlandırmak istediğinizi varsayalım:

temp_dict = {'k1':'v1', 'k2':'v2', 'k3':'v3'}
temp_dict['k4']= temp_dict.pop('k3')

1
Çalışmıyor, demek istediniz ki ... temp_dict ['k4'] = temp_dict.pop ('k3')?
DougR

Bu dönecektir TypeError: 'dict' object is not callableile If temp_dict('k3')sen değerini başvurmak için çalışıyoruz k3o zaman köşeli parantez değil parantez kullanmalıdır tuşu. Ancak, sözlüğe yeni bir anahtar eklenir ve istendiği gibi mevcut bir anahtar yeniden adlandırılmaz.
Yasemin
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.