Словари в Python: что нужно знать

Словари в Python: что нужно знать

4 апреля 2023 г.

Словари считаются краеугольным камнем Python и любого языка программирования. Стандартная библиотека Python особенно полезна, предоставляя готовые к использованию сопоставления, которые могут быть очень эффективными при правильном использовании.

Итак, в этой статье мы обсудим, что делает словари Python особенными.

Python Tutor execution

Понимание слова

Синтаксис понимания списка был адаптирован для словарей; dictComp создает экземпляр dict, беря пары {ключ: значение} из любого итерируемого объекта.

dial_codes = [
 (55, 'Brazil'),
 (86, 'China'),
 (91, 'India'),
 (62, 'Indonesia'),
 (81, 'Japan'),
 (7, 'Russia'),
 (1, 'United States'),
]

country_dial = {country: code for code, country in dial_codes}
print(country_dial)
# {'Brazil': 55, 'China': 86, 'India': 91, 'Indonesia': 62, 'Japan': 81, 'Russia': 7, 'United States': 1}

code_less_than_70 = {code: country.upper() for country, code in sorted(country_dial.items()) if code < 70}
print(code_less_than_70)
# {55: 'BRAZIL', 62: 'INDONESIA', 7: 'RUSSIA', 1: 'UNITED STATES'}

Распаковка сопоставлений

Распаковка словарей аналогична распаковке списка, только вы будете распаковывать пару

{key: value} и это делается с помощью ** вместо оператора ***.**

Необходимо сохранить несколько ключевых моментов:

* Мы можем применить оператор ** к более чем одному аргументу в вызове функции.

* Повторяющиеся аргументы ключевого слова запрещены в вызовах функций, поэтому убедитесь, что распакованные словарные ключи уникальны.

* Оператор ** можно использовать внутри литерала dict также несколько раз.

def dump(**kwargs):
    return kwargs

print(dump(**{'x': 1}, y=2, **{'z': 3}))
# {'x': 1, 'y': 2, 'z': 3}
print(dump(**{'x': 1}, y=2, **{'z': 3, 'x': 6}))
# TypeError: dump() got multiple values for keyword argument 'x'

print({'a': 0, **{'x': 1}, 'y': 2, **{'z': 3, 'x': 4}}
# Duplicate keys are allowed here and they get overwritten by the last occurence 
#{'a': 0, 'x': 4, 'y': 2, 'z': 3}

Объединение словарей

Словари поддерживают операторы Set Union:

* Оператор | создает новое сопоставление при слиянии.

* Оператор |= обновляет первый операнд при слиянии.

d1 = {'a': 1, 'b': 3}
d2 = {'a': 2, 'b': 4, 'c': 6}

print(d1 | d2)
# overwriting the last occurence {'a': 2, 'b': 4, 'c': 6}

d1 |= d2
print(d1)
# {'a': 2, 'b': 4, 'c': 6}

Автоматическая обработка отсутствующих ключей

Представьте себе сценарий, в котором программа пытается получить доступ к значению в словаре по своему ключу, но не находит его. Есть два способа справиться с этим.

Используйте defaultdict

Экземпляр defaultdict создает элементы со значением по умолчанию по запросу всякий раз, когда отсутствующий ключ ищется с использованием синтаксиса d[k].

from collections import defaultdict 
# Function to return a default values for keys that is not present
def def_value():
    return "This guest did not check in yet"
# Defining the dict
d = defaultdict(def_value)
d["guest_1"] = "Adam"
d["guest_2"] = "Eve"

print(d["guest_1"]) # Adam
print(d["guest_2"]) # Eve
print(d["guest_3"]) # This guest did not check in yet

Реализовать метод ***Отсутствует***

Мы также можем реализовать метод missing в dict или любом другом типе сопоставления.

class GuestDict(dict):
    def __missing__(self, key):
        return 'This guest did not check in yet'

x = {"guest_1": "Adam", "guest_2": "Eve"}
guest_dict = GuestDict(x)
# Try accessing missing key:
print(guest_dict['guest_3'])
# This guest did not check in yet

Просмотр словаря

Методы экземпляра dict .keys(), .values() и .items() возвращают экземпляры классов с именами dict_keys, dict_values и dict_items, которые доступны только для чтения, но являются динамическими прокси настоящий дикт.

Если исходный dict обновлен, вы можете немедленно увидеть изменения в существующем представлении.

d = {'a': 5, 'b': 2, 'c': 0, 'd': 7}
v = d.values()
print('values before before updating : ', v)
# values before before updating : dict_values([5, 2, 0, 7]) 
d['e'] = 5
print('Dictionary after adding new value', d)
# Dictionary after adding new value {'a': 5, 'b': 2, 'c': 0, 'd': 7, 'e': 5}

# dynamic view object is updated automatically
print('value of the updated dictionary: ', v)
# value of the updated dictionary: dict_values([5, 2, 0, 7, 5])

Различные варианты Dict

Существуют различные варианты dict, готовые к использованию, и мы можем использовать их, чтобы выиграть время.

коллекции.OrderedDict

Это обычные словари, но они имеют некоторые дополнительные возможности, связанные с операциями упорядочения; вы можете найти больше о них здесь.

коллекции.ChainMap

Экземпляр ChainMap содержит список сопоставлений, которые можно поискать как единое целое.

Поиск выполняется для каждого входного сопоставления в том порядке, в котором оно появляется в вызове конструктора, и завершается успешно, как только ключ находится в одном из этих сопоставлений.

baseline = {'music': 'bach', 'art': 'rembrandt'}
adjustments = {'art': 'van gogh', 'opera': 'carmen'}
print(list(ChainMap(adjustments, baseline)))
# ['music', 'art', 'opera']

коллекции.Счетчик

Сопоставление, которое содержит целое число для каждого ключа. Обновление существующего ключа увеличивает его количество.

ct = collections.Counter('abracadabra')
print(ct)
# Counter({'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1})
ct.update('aaaaazzz')
print(ct)
# Counter({'a': 10, 'z': 3, 'b': 2, 'r': 2, 'c': 1, 'd': 1})
print(ct.most_common(3))
# [('a', 10), ('z', 3), ('b', 2)]

полка.Полка

Модуль полки (название основано на том факте, что банки Pickle хранятся на полках) в стандартной библиотеке обеспечивает постоянное хранилище для сопоставления строковых ключей с объектами Python, сериализованными в двоичном формате Pickle.

UserDict

В UserDict есть несколько методов, готовых к использованию, вместо того, чтобы реализовывать или заново изобретать колесо, расширяя класс dict; подробнее об этом здесь.

MappingProxyType

Это класс-оболочка, который с учетом типа сопоставления возвращает экземпляр mappingproxy, который доступен только для чтения, но отражает изменения исходного сопоставления.

from types import MappingProxyType
d = {1: 'A'}
d_proxy = MappingProxyType(d)
print(d_proxy)
# mappingproxy({1: 'A'})
print(d_proxy[1])
# "A"
d[2] = 'B'
print(d_proxy) 
# mappingproxy({1: 'A', 2: 'B'})
print(d_proxy[2])
# "B"

Механика словаря Python

Реализация dict в Python очень эффективна, так как основана на хэш-таблице, но следует помнить о нескольких вещах.

* Ключи должны быть хешируемыми объектами. Они должны реализовать правильные методы **hash** и eq.

* Доступ к элементу по ключу очень быстрый. В словаре могут быть миллионы ключей, но Python может найти ключ напрямую, вычислив хэш-код ключа и получив смещение индекса в хэш-таблице.

* Лучше не создавать атрибуты экземпляра dict вне метода init, так как это экономит память.

* Dicts неизбежно имеют значительные накладные расходы памяти.

Дополнительная литература


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE