Поздравляем. Вы также обнаружили бесполезность стандартного типа collections.defaultdict
.Если эта выполнимая средняя куча запаха кода оскорбляет ваши тонкие чувства так же, как и мои, это ваш счастливый день StackOverflow.
Благодаря запретному чуду 3-вариант параметра встроенной функции type()
, создание бесполезного типа словаря по умолчанию - это весело и выгодно.
Что не так с dict .__ отсутствует __ ()?
Абсолютно ничего,Предполагая, что вам нравится лишний шаблон и шокирующая глупость collections.defaultdict
- который должен вести себя как ожидалось, но на самом деле это не так.Справедливости ради, Йохен Ритцель принял решение подкласса dict
и реализацию необязательного __missing__()
метода - фантастикаОбходной путь для небольших случаев использования, требующих только один словарь по умолчанию.
Но шаблон такого рода плохо масштабируется.Если вы обнаружите, что создаете несколько словарей по умолчанию, каждый со своей немного отличной логикой для генерации недостающих пар ключ-значение, то вам необходим промышленный образец альтернативной автоматизации.
Или, по крайней мере, приятно.Почему бы не исправить то, что сломано?
Введение DefaultDict
Менее чем в десяти строках чистого Python (исключая строки документации, комментарии и пробелы), теперь мы определяем тип DefaultDict
, инициализированный с помощьюопределяемый пользователем вызываемый генерирующий значения по умолчанию для отсутствующих ключей.В то время как вызываемый элемент, переданный стандартному типу collections.defaultdict
, бесполезно принимает параметры no , вызываемый элемент, передаваемый нашему типу DefaultDict
, с пользой принимает следующие два параметра:
- Текущий экземплярэтого словаря.
- Текущий пропущенный ключ для создания значения по умолчанию для.
Учитывая этот тип, решение вопроса sorin сводится к одной строкеPython:
>>> dic = DefaultDict(lambda self, missing_key: missing_key)
>>> dic['a'] = 'one a'
>>> print(dic['a'])
one a
>>> print(dic['b'])
b
Разумность. Наконец.
Код или не случилось
def DefaultDict(keygen):
'''
Sane **default dictionary** (i.e., dictionary implicitly mapping a missing
key to the value returned by a caller-defined callable passed both this
dictionary and that key).
The standard :class:`collections.defaultdict` class is sadly insane,
requiring the caller-defined callable accept *no* arguments. This
non-standard alternative requires this callable accept two arguments:
#. The current instance of this dictionary.
#. The current missing key to generate a default value for.
Parameters
----------
keygen : CallableTypes
Callable (e.g., function, lambda, method) called to generate the default
value for a "missing" (i.e., undefined) key on the first attempt to
access that key, passed first this dictionary and then this key and
returning this value. This callable should have a signature resembling:
``def keygen(self: DefaultDict, missing_key: object) -> object``.
Equivalently, this callable should have the exact same signature as that
of the optional :meth:`dict.__missing__` method.
Returns
----------
MappingType
Empty default dictionary creating missing keys via this callable.
'''
# Global variable modified below.
global _DEFAULT_DICT_ID
# Unique classname suffixed by this identifier.
default_dict_class_name = 'DefaultDict' + str(_DEFAULT_DICT_ID)
# Increment this identifier to preserve uniqueness.
_DEFAULT_DICT_ID += 1
# Dynamically generated default dictionary class specific to this callable.
default_dict_class = type(
default_dict_class_name, (dict,), {'__missing__': keygen,})
# Instantiate and return the first and only instance of this class.
return default_dict_class()
_DEFAULT_DICT_ID = 0
'''
Unique arbitrary identifier with which to uniquify the classname of the next
:func:`DefaultDict`-derived type.
'''
Ключ ... получить его, ключ ? к этому тайному волшебству относится вызов трехпараметрического варианта встроенного type()
:
type(default_dict_class_name, (dict,), {'__missing__': keygen,})
Эта единственная строка динамически генерируетновый dict
подкласс псевдоним необязательного метода __missing__
для вызываемого абонента вызываемого объекта.Обратите внимание на явное отсутствие шаблона, сокращающее использование DefaultDict
до одной строки Python.
Автоматизация для вопиющего выигрыша.