sor*_*rin 54 python dictionary data-structures dictionary-missing
我想创建一个python字典,它返回字典中缺少键的键值.
用法示例:
dic = smart_dict()
dic['a'] = 'one a'
print(dic['a'])
# >>> one a
print(dic['b'])
# >>> b
Run Code Online (Sandbox Code Playgroud)
Joc*_*zel 65
dict
有一个__missing__
钩子:
class smart_dict(dict):
def __missing__(self, key):
return key
Run Code Online (Sandbox Code Playgroud)
Dar*_*mas 26
你为什么不用它
dic.get('b', 'b')
Run Code Online (Sandbox Code Playgroud)
当然,你可以dict
像其他人指出的那样进行子类化,但我发现每次偶尔提醒自己get
可以有一个默认值!
如果你想去defaultdict
,请试试这个:
dic = defaultdict()
dic.__missing__ = lambda key: key
dic['b'] # should set dic['b'] to 'b' and return 'b'
Run Code Online (Sandbox Code Playgroud)
除了......好吧AttributeError: ^collections.defaultdict^object attribute '__missing__' is read-only
,所以你必须要子类:
from collections import defaultdict
class KeyDict(defaultdict):
def __missing__(self, key):
return key
d = KeyDict()
print d['b'] #prints 'b'
print d.keys() #prints []
Run Code Online (Sandbox Code Playgroud)
Ray*_*ger 13
第一个受访者提到defaultdict
,但您可以__missing__
为以下任何子类定义dict
:
>>> class Dict(dict):
def __missing__(self, key):
return key
>>> d = Dict(a=1, b=2)
>>> d['a']
1
>>> d['z']
'z'
Run Code Online (Sandbox Code Playgroud)
另外,我喜欢第二个受访者的方法:
>>> d = dict(a=1, b=2)
>>> d.get('z', 'z')
'z'
Run Code Online (Sandbox Code Playgroud)
恭喜.你也发现了标准collections.defaultdict
类型的无用性.如果那令人难以忍受的代码堆中的气味
会像我一样冒犯你敏感的感觉,那么这就是你幸运的StackOverflow日.
多亏了的禁奇迹3参数变异的的type()
内置,制作一个非无用默认字典类型是既有趣又有利可图.
绝对没有,假设你喜欢过多的样板和令人震惊的愚蠢collections.defaultdict
- 这应该像预期的那样表现但实际上并非如此.为了公平起见,约亨Ritzel的接受的解决方案的子类化dict
和实施可选的__missing__()
方法 是小规模使用的情况下,仅需要一个默认的字典一个梦幻般的解决方法.
但是这种样板很难扩展.如果您发现自己实例化多个默认词典,每个词典都有自己稍微不同的逻辑来生成缺失的键值对,那么就需要一个工业级的替代自动化样板.
或者至少很好.因为为什么不修理坏了什么?
在不到十行纯Python(不包括文档字符串,注释和空格)中,我们现在定义一个DefaultDict
用用户定义的可调用项初始化的类型,为缺少的键生成默认值.传递给标准collections.defaultdict
类型
的callable 无用地接受没有参数,传递给我们DefaultDict
类型的callable有用地接受以下两个参数:
鉴于这种类型,解决sorin的问题减少到单行Python:
>>> dic = DefaultDict(lambda self, missing_key: missing_key)
>>> dic['a'] = 'one a'
>>> print(dic['a'])
one a
>>> print(dic['b'])
b
Run Code Online (Sandbox Code Playgroud)
理智.最后.
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.
'''
Run Code Online (Sandbox Code Playgroud)
关键......得到它,关键?对这个神秘的魔法是
对内置的3参数变体的type()
调用:
type(default_dict_class_name, (dict,), {'__missing__': keygen,})
Run Code Online (Sandbox Code Playgroud)
此单行动态生成一个新dict
子类,将可选__missing__
方法别名化为调用者定义的可调用方法.注意明显缺乏样板,减少DefaultDict
了对单行Python的使用.
自豪的胜利.
我同意这应该很容易做到,并且也很容易使用不同的默认值或以某种方式转换缺失值的函数进行设置。
受到Cecil Curry答案的启发,我问自己:为什么不将默认生成器(常量或可调用)作为类的成员,而不是一直生成不同的类?让我演示一下:
# default behaviour: return missing keys unchanged
dic = FlexDict()
dic['a'] = 'one a'
print(dic['a'])
# 'one a'
print(dic['b'])
# 'b'
# regardless of default: easy initialisation with existing dictionary
existing_dic = {'a' : 'one a'}
dic = FlexDict(existing_dic)
print(dic['a'])
# 'one a'
print(dic['b'])
# 'b'
# using constant as default for missing values
dic = FlexDict(existing_dic, default = 10)
print(dic['a'])
# 'one a'
print(dic['b'])
# 10
# use callable as default for missing values
dic = FlexDict(existing_dic, default = lambda missing_key: missing_key * 2)
print(dic['a'])
# 'one a'
print(dic['b'])
# 'bb'
print(dic[2])
# 4
Run Code Online (Sandbox Code Playgroud)
它是如何工作的?没那么难:
class FlexDict(dict):
'''Subclass of dictionary which returns a default for missing keys.
This default can either be a constant, or a callable accepting the missing key.
If "default" is not given (or None), each missing key will be returned unchanged.'''
def __init__(self, content = None, default = None):
if content is None:
super().__init__()
else:
super().__init__(content)
if default is None:
default = lambda missing_key: missing_key
self.default = default # sets self._default
@property
def default(self):
return self._default
@default.setter
def default(self, val):
if callable(val):
self._default = val
else: # constant value
self._default = lambda missing_key: val
def __missing__(self, x):
return self.default(x)
Run Code Online (Sandbox Code Playgroud)
当然,人们可以争论是否希望允许在初始化后更改默认函数,但这只是意味着将@default.setter
其逻辑删除并吸收到__init__
.
可以添加两行额外的代码来启用对当前(常量)默认值的内省。