【问题标题】:Python dictionary with default key具有默认键的 Python 字典
【发布时间】:2016-01-28 22:14:24
【问题描述】:

我正在运行 Python 2.7.10。

我想让字典返回存储在特定键中的值,以防丢失项目。例如,类似的东西:

myD = dict(...)
return myD[key] if key in myD else myD[defaultKey]

为了确保清楚,我想调用 myD[key] 并返回正确的值,而我的代码中没有额外的 if...else...

这不是defaultdict 所做的(因为它需要一个函数作为默认调用),也不是dict.setdefault() 所做的,myD.get(key, ???) 似乎也没有帮助。我可能应该从dictdefaultdict 继承并重载__init__()missing() 方法,但我想不出一个好方法来做到这一点。

【问题讨论】:

  • 您的问题是什么?你说得对,dict.get 不会那样做;如果您想知道如何编写自己的代码,请查看docs.python.org/2/library/… 并相应地实现__getitem__。但是defaultKey 应该从哪里来(是参数吗?是类的属性,还是实例的属性?)
  • @jonrsharpe 是的,这是我的问题,如何实现。我不确定如何在这种情况下使用 ABC
  • @jonrsharpe 是的,defaultKey 是初始化时的参数,实例应存储为属性
  • 您继承它们,然后实现它们指定的方法(在本例中为 __[get/set/del]item____iter____len__,加上您自定义的 __init__ 用于默认键值对)。
  • @jonrsharpe 我希望我可以通过从一个现成的对象(如defaultdict)继承来获得一些更简单的东西,然后覆盖__getitem__,在其他地方留下相同的功能......

标签: python python-2.7 dictionary default default-value


【解决方案1】:

在你的情况下,dict.get 应该可以工作(我知道你已经提到它不起作用)。你试过了吗:

myD.get(key,myD[defaultKey])

【讨论】:

  • 是的,这会起作用,但我需要创建一个默认情况下会执行此操作的类,而无需我在调用代码中明确指定它
  • @gt6989b 那么,你的问题是什么?如果它只是“我该怎么写?”它对 SO 来说太宽泛了。
  • 您是否尝试过创建一个继承defaultdict 但覆盖defaultfactory 方法的类?
  • 我同意罗伯特的观点。在这种情况下,创建您自己的字典实现并将其.get() 覆盖为上述代码
  • defaultfactory 实际上将 dict 更改为具有新内容。 get 返回对现有事物的引用。所以取决于你想要什么......
【解决方案2】:

我不完全确定您想要什么(没有阅读所有您问题下的 cmets),但认为这可能至少接近您想要的。

class DefaultKeyDict(dict):
    def __init__(self, default_key, *args, **kwargs):
        self.default_key = default_key
        super(DefaultKeyDict, self).__init__(*args, **kwargs)

    def __missing__ (self, key):
        if self.default_key not in self:  # default key not defined
            raise KeyError(key)
        return self[self.default_key]

    def __repr__(self):
        return ('{}({!r}, {})'.format(self.__class__.__name__,
                                      self.default_key,
                                      super(DefaultKeyDict, self).__repr__()))

    def __reduce__(self):  # optional, for pickle support
        args = (self.default_key if self.default_key in self else None,)
        return self.__class__, args, None, None, self.iteritems()

dkdict = DefaultKeyDict('b', {'a': 1, 'b': 2, 'c': 3})

print dkdict['a']  # -> 1
print dkdict['b']  # -> 2
print dkdict['c']  # -> 3
print dkdict['d']  # -> 2 (value of the default key)

del dkdict['b']  # delete the default key
try:
    print dkdict['d']  # should now raise exception like normal
except KeyError:
    print("dkdict['d'] raised a KeyError")

您可能希望修改类__init__() 方法以接受默认键及其值作为参数(而不仅仅是键)。

【讨论】:

  • 谢谢,我一直在玩弄这个以使其正常工作,但无法正确完成...非常感谢。如果我想按照您的建议在__init__() 中传递一个arg default_key,签名将更改为def __init__(self, default_key, *args, **kwargs)?还是我只是检查default_key 是否在kwargs 中(如果是,我如何才能将其与字典本身中真正想要这样一对的人区分开来)?
  • 为了记录,规范的哨兵(当None由于是一个有效值而不合适时)是只做sentinel = object()sentinel可以是Null,如果你喜欢,但我发现sentinel 更直接地描述了目的)并与is 进行了所有比较(主要是为了提高效率;object__eq__ 无论如何都是身份检查)。无需创建相对昂贵的类来定义哨兵类型; object 可以直接构造以使用几乎不使用任何资源的唯一标记(对象实例在 Py 3.5 上仅为 16 个字节,而对于类 + 实例则为 1016B+56B)。
  • @ShadowRanger:要点...虽然我并不太关心额外的内存使用,因为只创建了一个实例。它不是哨兵,因为它不标记任何事物的开始或结束,所以我没有给它起这个名字。
  • @gt6989b:您可以将签名 def __init__(self, default_key, default_key_value, *args, **kwargs) 设为关键字参数。为了将后者与用户键/值对区分开来,关键字可以以下划线开头,即_default_key=('DefaultKey', 42)。按照惯例,这样做是在 Python 中通常指定私有名称的方式。
【解决方案3】:

当覆盖__getitem__ 时,可以使用简单的方括号。它返回第一个有效键的值,如果没有找到键,则返回 None

class mDict(dict):
    def __getitem__(self, keys):
        for k in keys:
            if k in self:
                return self.get(k)

mdict = mDict({'a': 1, 'b': 2, 'default': 3})

mdict['a', 'default']        # -> 1
mdict['X', 'b', 'default']   # -> 2
mdict['X', 'Y', 'default']   # -> 3
mdict['X', 'Y', 'Z']         # -> None

这里还可以使用两个以上的键,这比许多嵌套的.get() 更具可读性。

【讨论】:

  • 虽然不确定这是否真的回答了问题
猜你喜欢
  • 1970-01-01
  • 2016-06-25
  • 2017-09-06
  • 2011-05-04
  • 1970-01-01
  • 2011-06-29
  • 1970-01-01
  • 1970-01-01
  • 2014-09-08
相关资源
最近更新 更多