İki Python sözlüğünü tek bir ifadede nasıl birleştirebilirim?
Sözlüklerde için x
ve y
, z
bir sığ değerlerle Sözlük birleşti olur y
gelenler değiştirilmesi x
.
Python 3.5 veya üstü sürümlerde:
z = {**x, **y}
Python 2'de (veya 3.4 veya daha düşük) bir işlev yazın:
def merge_two_dicts(x, y):
z = x.copy() # start with x's keys and values
z.update(y) # modifies z with y's keys and values & returns None
return z
ve şimdi:
z = merge_two_dicts(x, y)
Python 3.9.0a4 veya daha büyük sürümlerde (son yayın tarihi yaklaşık Ekim 2020): Burada tartışılan PEP-584 , bunu daha da basitleştirmek için uygulandı:
z = x | y # NOTE: 3.9+ ONLY
açıklama
İki dikteniz olduğunu ve orijinal dikleri değiştirmeden bunları yeni bir dikte birleştirmek istediğinizi varsayalım:
x = {'a': 1, 'b': 2}
y = {'b': 3, 'c': 4}
İstenen sonuç, z
birleştirilen değerler ve ikinci diktenin değerleri birinciden bunların üzerine yazılan yeni bir sözlük ( ) elde etmektir.
>>> z
{'a': 1, 'b': 3, 'c': 4}
Önerilen bunun için yeni bir sözdizimi, PEP 448 ve Python 3.5 itibariyle mevcut olduğu
z = {**x, **y}
Ve aslında tek bir ifadedir.
Gerçek gösterimle de birleştirebileceğimizi unutmayın:
z = {**x, 'foo': 1, 'bar': 2, **y}
ve şimdi:
>>> z
{'a': 1, 'b': 3, 'foo': 1, 'bar': 2, 'c': 4}
Şimdi 3.5, PEP 478 sürüm programında uygulandığı şekliyle gösteriliyor ve şimdi Python 3.5 belgesindeki Yenilikler belgesine girdi .
Ancak, birçok kuruluş hala Python 2'de olduğu için, bunu geriye dönük uyumlu bir şekilde yapmak isteyebilirsiniz. Python 2 ve Python 3.0-3.4'te bulunan klasik Pythonic yolu, bunu iki adımlı bir işlem olarak yapmaktır:
z = x.copy()
z.update(y) # which returns None since it mutates z
Her iki yaklaşımda da y
ikinci olacak ve değerleri x
'nin değerlerinin yerini alacak , böylece nihai sonucumuza 'b'
işaret edecektir 3
.
Henüz Python 3.5'te değil, tek bir ifade istiyorum
Henüz Python 3.5'te değilseniz veya geriye dönük uyumlu bir kod yazmanız gerekiyorsa ve bunu tek bir ifadede istiyorsanız , en doğru performans en doğru yaklaşım onu bir işleve koymaktır:
def merge_two_dicts(x, y):
"""Given two dicts, merge them into a new dict as a shallow copy."""
z = x.copy()
z.update(y)
return z
ve sonra tek bir ifadeniz var:
z = merge_two_dicts(x, y)
Ayrıca, sıfırdan çok büyük bir sayıya kadar tanımsız sayıda dikteyi birleştirmek için bir işlev de yapabilirsiniz:
def merge_dicts(*dict_args):
"""
Given any number of dicts, shallow copy and merge into a new dict,
precedence goes to key value pairs in latter dicts.
"""
result = {}
for dictionary in dict_args:
result.update(dictionary)
return result
Bu işlev tüm dikteler için Python 2 ve 3'te çalışacaktır. Verilen dicts örneğin a
için g
:
z = merge_dicts(a, b, c, d, e, f, g)
ve anahtar değer çiftleri g
dicts göre öncelikli olacağını a
etmek f
, vb.
Diğer Cevapların Eleştirileri
Daha önce kabul edilen cevapta gördüklerinizi kullanmayın:
z = dict(x.items() + y.items())
Python 2'de, her bir dikte için bellekte iki liste oluşturursunuz, bellekte bir araya getirilen ilk ikinin uzunluğuna eşit uzunlukta üçüncü bir liste oluşturursunuz ve daha sonra dikteyi oluşturmak için üç listeyi de atarsınız. Python 3'te,dict_items
iki liste değil, iki nesne eklediğiniz için bu başarısız olur -
>>> c = dict(a.items() + b.items())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'dict_items' and 'dict_items'
ve bunları açıkça liste olarak oluşturmanız gerekir, örn z = dict(list(x.items()) + list(y.items()))
. Bu, kaynakların ve hesaplama gücünün israfıdır.
Benzer şekilde, değerler paylaşılamayan nesneler (örneğin listeler gibi) olduğunda items()
Python 3'te ( viewitems()
Python 2.7'de) birleşimi de başarısız olur. Değerleriniz yıkanabilir olsa bile, kümeler semantik olarak sıralanmamış olduğundan, davranış önceliğe göre tanımlanmamıştır. Yani bunu yapma:
>>> c = dict(a.items() | b.items())
Bu örnek, değerler paylaşılamadığında ne olacağını gösterir:
>>> x = {'a': []}
>>> y = {'b': []}
>>> dict(x.items() | y.items())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
İşte y'nin önceliğe sahip olması gereken bir örnek, ancak bunun yerine x'in değeri, kümelerin keyfi sırası nedeniyle korunur:
>>> x = {'a': 2}
>>> y = {'a': 1}
>>> dict(x.items() | y.items())
{'a': 2}
Kullanmamanız gereken başka bir kesmek:
z = dict(x, **y)
Bu, yapıcıyı kullanır dict
ve çok hızlı ve bellek verimlidir (iki aşamalı işlemimizden biraz daha fazla), ancak burada ne olduğunu tam olarak bilmediğiniz sürece (yani, ikinci diksiyon, dikteye anahtar kelime argümanları olarak geçiriliyorsa) yapıcı), okumak zor, amaçlanan kullanım değildir ve Pythonic değildir.
Django'da düzeltilen kullanımın bir örneği .
Diktelerin, yıkanabilir anahtarlar alması amaçlanmıştır (örn. Frozensets veya tuples), ancak anahtarlar dizgisiz olduğunda bu yöntem Python 3'te başarısız olur.
>>> c = dict(a, **b)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings
Gönderen posta listesine , Guido van Rossum, dilin yaratıcısı yazdı:
Sonuçta ** mekanizmasını kötüye kullandığından, dikt ({}, ** {1: 3}) yasadışı ilan etmekte iyiyim.
ve
Görünüşe göre (x, ** y), "x.update (y) çağrısı ve x döndürme" için "cool hack" olarak dolaşıyor. Şahsen ben serinden daha aşağılık buluyorum.
Benim amacım ( ve dilin yaratıcısının anlaşılması ) amaçlanan kullanımın dict(**y)
okunabilirlik amaçları için dikte oluşturmaktır, örneğin:
dict(a=1, b=10, c=11)
onun yerine
{'a': 1, 'b': 10, 'c': 11}
Yorumlara yanıt
Guido'nun söylediklerine rağmen dict(x, **y)
, btw olan dikt şartnamesi ile uyumludur. Hem Python 2 hem de 3 için çalışır. Bunun yalnızca dize anahtarları için çalıştığı gerçeği, anahtar kelime parametrelerinin nasıl çalıştığının doğrudan bir sonucudur ve kısa bir açıklama değildir. ** operatörünü de bu yerde mekanizmanın kötüye kullanılması olarak kullanmıyor, aslında ** tam olarak anahtar kelimeleri anahtar olarak aktarmak için tasarlandı.
Yine, anahtarlar dizgisiz olduğunda 3 için çalışmaz. Örtük çağrı sözleşmesi, ad alanlarının normal dikte alması ve kullanıcıların yalnızca dize olan anahtar kelime bağımsız değişkenlerini iletmesi gerektiğidir. Diğer tüm callables bunu zorladı. dict
Python 2'de bu tutarlılığı kırdı:
>>> foo(**{('a', 'b'): None})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() keywords must be strings
>>> dict(**{('a', 'b'): None})
{('a', 'b'): None}
Python'un (Pypy, Jython, IronPython) diğer uygulamaları dikkate alındığında bu tutarsızlık kötüydü. Böylece Python 3'te düzeltildi, çünkü bu kullanım kırıcı bir değişiklik olabilir.
Size bir dilin yalnızca bir sürümünde çalışan veya yalnızca belirli rasgele kısıtlamalar verildiğinde çalışan kodların kasıtlı olarak yazılmasının kötü amaçlı bir yetersizlik olduğunu size bildiririm.
Diğer yorumlar:
dict(x.items() + y.items())
hala Python 2 için en okunabilir çözümdür. Okunabilirlik önemlidir.
Cevabım: merge_two_dicts(x, y)
aslında okunabilirlik konusunda endişeliysek bana çok daha açık görünüyor. Ve Python 2 gittikçe kullanımdan kaldırıldığı için ileri uyumlu değildir.
{**x, **y}
iç içe sözlükleri işlemiyor gibi görünmüyor. iç içe anahtarların içeriği sadece üzerine yazılır, birleştirilmez [...] Yinelenen bu cevaplar tarafından yakıldım ve kimsenin bahsetmediğine şaşırdım. "Birleştirme" kelimesini yorumladığımda, bu cevaplar "bir dikteyi diğeriyle güncellemek" ve birleşmemek anlamına geliyor.
Evet. Sizi iki kişilik sığ birleşme isteyen soruya geri yönlendirmeliyim sözlükten ilkinin değerleri ikincinin değerleri üzerine yazılan tek bir ifadede .
İki sözlük sözlüğü varsayarsak, biri bunları tek bir işlevde yinelemeli olarak birleştirebilir, ancak her iki kaynaktan gelen dikmeleri değiştirmemeye dikkat etmelisiniz ve bundan kaçınmanın en kesin yolu, değerleri atarken bir kopya yapmaktır. Anahtarların yıkanabilir olması ve genellikle değiştirilememesi gerektiğinden, bunları kopyalamak anlamsızdır:
from copy import deepcopy
def dict_of_dicts_merge(x, y):
z = {}
overlapping_keys = x.keys() & y.keys()
for key in overlapping_keys:
z[key] = dict_of_dicts_merge(x[key], y[key])
for key in x.keys() - overlapping_keys:
z[key] = deepcopy(x[key])
for key in y.keys() - overlapping_keys:
z[key] = deepcopy(y[key])
return z
Kullanımı:
>>> x = {'a':{1:{}}, 'b': {2:{}}}
>>> y = {'b':{10:{}}, 'c': {11:{}}}
>>> dict_of_dicts_merge(x, y)
{'b': {2: {}, 10: {}}, 'a': {1: {}}, 'c': {11: {}}}
Diğer değer türleri için beklenmedik durumlarla karşılaşmak bu sorunun kapsamı dışındadır, bu yüzden sizi "sözlüklerin birleştirilmesi sözlükleri" hakkındaki kanonik soruya vereceğim yanıta işaret edeceğim .
Daha Az Performanslı Ama Doğru Ad-hocs
Bu yaklaşımlar daha az performans gösterir, ancak doğru davranış sağlayacaktır. Onlar olacak çok daha az ölçülebilir daha copy
ve update
yeni açma onlar daha yüksek bir soyutlama düzeyinde her anahtar-değer çifti üzerinden yineleme çünkü ya, ama onlar yapmak öncelik sırasını (ikinci dicts önceliğe sahip) saygı
Bir dikseli kavrama içinde dikleri manuel olarak zincirleyebilirsiniz:
{k: v for d in dicts for k, v in d.items()} # iteritems in Python 2.7
veya python 2.6'da (ve belki de jeneratör ifadeleri eklendiğinde 2.4 kadar erken):
dict((k, v) for d in dicts for k, v in d.items())
itertools.chain
yineleyicileri anahtar / değer çiftleri üzerinde doğru sırada zincirler:
import itertools
z = dict(itertools.chain(x.iteritems(), y.iteritems()))
Performans analizi
Sadece doğru davrandığı bilinen kullanımların performans analizini yapacağım.
import timeit
Ubuntu 14.04'te aşağıdakiler yapılır
Python 2.7'de (sistem Python):
>>> min(timeit.repeat(lambda: merge_two_dicts(x, y)))
0.5726828575134277
>>> min(timeit.repeat(lambda: {k: v for d in (x, y) for k, v in d.items()} ))
1.163769006729126
>>> min(timeit.repeat(lambda: dict(itertools.chain(x.iteritems(), y.iteritems()))))
1.1614501476287842
>>> min(timeit.repeat(lambda: dict((k, v) for d in (x, y) for k, v in d.items())))
2.2345519065856934
Python 3.5'te (deadsnakes PPA):
>>> min(timeit.repeat(lambda: {**x, **y}))
0.4094954460160807
>>> min(timeit.repeat(lambda: merge_two_dicts(x, y)))
0.7881555100320838
>>> min(timeit.repeat(lambda: {k: v for d in (x, y) for k, v in d.items()} ))
1.4525277839857154
>>> min(timeit.repeat(lambda: dict(itertools.chain(x.items(), y.items()))))
2.3143140770262107
>>> min(timeit.repeat(lambda: dict((k, v) for d in (x, y) for k, v in d.items())))
3.2069112799945287
Sözlüklerdeki Kaynaklar
z = x | y