Python sözlüğündeki değerleri eşleme


243

Bir sözlük verildiğinde { k1: v1, k2: v2 ... }almak istiyorum { k1: f(v1), k2: f(v2) ... }bir işlevi geçmek f.

Böyle yerleşik bir işlev var mı? Yoksa yapmak zorunda mıyım

dict([(k, f(v)) for (k, v) in my_dictionary.iteritems()])

İdeal olarak sadece yazardım

my_dictionary.map_values(f)

veya

my_dictionary.mutate_values_with(f)

Yani, orijinal sözlüğün mutasyona uğraması veya bir kopyasının oluşturulması benim için önemli değil.


2
Örneğinizi yazmanın daha iyi bir yolu dict((k, f(v)) for k, v in mydict.iteritems()), yani köşeli parantezler olmadan, bir jeneratör aracılığıyla bir ara liste oluşturulmasını engelleyecektir.
bereal

Yanıtlar:


354

Böyle bir işlev yoktur; bunu yapmanın en kolay yolu dik bir kavrayış kullanmaktır:

my_dictionary = {k: f(v) for k, v in my_dictionary.items()}

Python 2.7'de, belleği kaydetmek .iteritems()yerine yöntemi kullanın .items(). Diksiyon anlama sözdizimi python 2.7'ye kadar kullanılmadı.

Listelerde de böyle bir yöntem olmadığını unutmayın; bir liste kavrama veya map()işlev kullanmanız gerekir.

Bu nedenle, map()sözlüğü de işlemek için kullanabilirsiniz :

my_dictionary = dict(map(lambda kv: (kv[0], f(kv[1])), my_dictionary.iteritems()))

ama bu gerçekten okunaklı değil.


5
+1: Ben de bunu yapardım. dict(zip(a, map(f, a.values())))marjinal olarak daha kısadır, ancak ne yaptığını düşünmek zorundayım ve kendime şunu hatırlatmalıyım: evet, anahtarlar ve değerler, söz konusu değişiklik değişmezse aynı sırayla yinelenir. Diktecinin ne yaptığını hiç düşünmem gerekmiyor, bu yüzden doğru cevap bu.
DSM

2
@chiborg: Bunun nedeni, tüm anahtar / değer çiftlerini tek seferde aramak yerine, artık anahtar sayısı kez my_dictionary.__getitem__çağrılarını kullanmanızdır.
Martijn Pieters

1
Çünkü o Not PEP3113 (piton 3.x uygulanan) tanımlama grubu parametreleri artık desteklenmez: lambda (k,v): (k, f(v))gibi bir şey için yeniden yazılması içinlambda k_v: (k_v[0], f(k_v[1]))
normanius

1
Parametre paketini açma neden karıştırıldı? Bu nasıl bir gelişme ?
javadba

3
FP dilinden gelen Python inanılmaz derecede garip görünüyor.
juanchito


21

Büyük sözlükler için tercih edilebilecek yeni bir diksiyon oluşturmak yerine bunu yerinde yapabilirsiniz (bir kopyaya ihtiyacınız yoksa).

def mutate_dict(f,d):
    for k, v in d.iteritems():
        d[k] = f(v)

my_dictionary = {'a':1, 'b':2}
mutate_dict(lambda x: x+1, my_dictionary)

my_dictionaryiçeren sonuçlar :

{'a': 2, 'b': 3}

1
Serin, belki yeniden adlandırmak gerekir mapdictiçin mutate_values_withkristal Eğer dicti yeniden o temizlemek yapmak ya da bir şey! :)
Tarrasch

2
zip(d.keys(), d.values())yerine daha fazla sürüm için çalışıriteritems()
ytpillai

1
@ytpillai 'zip' veya anlama, cevabımın amacı olan değerleri yerinde değiştirmek yerine bir kopya oluşturur. Kabul edilen cevap, bir kopya uygun olduğunda en iyisidir.
gens

1
Özür dilerim, items yöntemini kullanmak istediğini fark etmedim. Bununla birlikte, bunun için başka bir gelişme de mümkündür (Python 2.7 olmayan kullanıcılar için){k:f(v) for k,v in iter(d.items())}
ytpillai

1
Bir yineleyici yaparak yerden
tasarruf sağlar


4

Orijinal cevabım bu noktayı kaçırmış olsa da (bu sorunu defaultdict fabrikasındaki Access anahtarına çözüm ile çözmeye çalışarak ), mevcut soruya gerçek bir çözüm önermek için yeniden çalıştım.

İşte burada:

class walkableDict(dict):
  def walk(self, callback):
    try:
      for key in self:
        self[key] = callback(self[key])
    except TypeError:
      return False
    return True

Kullanımı:

>>> d = walkableDict({ k1: v1, k2: v2 ... })
>>> d.walk(f)

Fikir, orijinal diksele istenen işlevselliği vermek için alt sınıfı sınıflandırmaktır: bir işlevi tüm değerler üzerinde "eşleme".

Artı nokta, bu sözlüğün orijinal verileri olduğu gibi depolamak için kullanılabilirken dict, talep üzerine herhangi bir veriyi geri arama ile dönüştürmek için kullanılabilir.

Tabii ki, sınıfı ve işlevi istediğiniz gibi adlandırmaktan çekinmeyin (bu cevapta seçilen ad PHP'nin array_walk()işlevinden esinlenmiştir ).

Not: Ne try- exceptbloğu ne de returnifadeler işlevsellik için zorunlu değildir, PHP'lerin davranışlarını daha fazla taklit etmek için oradadırlar array_walk.


1
Bu, OP sorusunu çözemediğinden, __missing__dönüştürmek istediğimiz mevcut anahtarlar için yöntem çağrılmayacak, çünkü fabrika yöntemi geçtiğinde başlangıç ​​noktası bir şekilde geri dönüş olarak kullanılmıyorsa, ancak örnek kullanımın bir parçası olmadığı için, Bunu eldeki soruna tatmin edici olmayan bir cevap olarak görüyorum.
Kaos

Hangi mevcut anahtarlar?
7heo.tk

OP Gönderen: Given a dictionary { k1: v1, k2: v2 ... } .... Yani, zaten bir başlangıç ​​var dict..
Kaos

İkimizin de haklı olduğunu söylemek isterim; ama ikimizin de yanlış olduğuna inanıyorum. Cevabım soruyu cevaplamadığı için haklısın; ama davet ettiğiniz sebepten dolayı değil. Ben sadece bir noktaya, {v1: f(v1), v2: f(v2), ...}verilen almak için bir yol vererek [v1, v2, ...], ve bir diksiyon vermedi özledim . Bunu düzeltmek için cevabımı düzenleyeceğim.
7heo.tk

2

Lambda içinden dizinleme yapmaktan kaçınmak için, örneğin:

rval = dict(map(lambda kv : (kv[0], ' '.join(kv[1])), rval.iteritems()))

Ayrıca şunları da yapabilirsiniz:

rval = dict(map(lambda(k,v) : (k, ' '.join(v)), rval.iteritems()))

Bu, ikinci örnekte 2 parçanın içindeki akıllı bir manipülasyon. Bununla birlikte, artık Python 3'te desteklenmeyen lambda içinde otomatik demet açma kullanır. Bu nedenle lambda(k,v)çalışmaz. Bkz. Stackoverflow.com/questions/21892989/…
Jonathan Komar

0

Bu kullanım durumuyla karşılaştım. Gens'in cevabını uygulayarak , aynı zamanda dikte edilen değerlerin işlenmesi için tekrarlayan bir yaklaşım ekledim:

def mutate_dict_in_place(f, d):
    for k, v in d.iteritems():
        if isinstance(v, dict):
            mutate_dict_in_place(f, v)
        else:
            d[k] = f(v)

# Exemple handy usage
def utf8_everywhere(d):
    mutate_dict_in_place((
        lambda value:
            value.decode('utf-8')
            if isinstance(value, bytes)
            else value
        ),
        d
    )

my_dict = {'a': b'byte1', 'b': {'c': b'byte2', 'd': b'byte3'}}
utf8_everywhere(my_dict)
print(my_dict)

Bu, Python 2'de dizeleri bayt olarak kodlayan json veya yaml dosyalarıyla uğraşırken yararlı olabilir

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.