Python - 函数类似于dict.get,但返回默认键的值而不是默认值?

3
假设我们有一个...
a = {1:2, 3:4, 5:6}

是否有内置函数(可能类似于 a.get2(7,5))可以返回a[7],如果a[7]不存在,则返回a[5]?

如果存在内置解决方案,则可以轻松定义此类功能为a.get(val, a.get(def_key)),但希望使用内置解决方案。


为了参考,这里是如何格式化代码块,供您将来提问时使用。 - user202729
4
并非所有能够想象得到的操作都有内置函数可以节省 2 个字符。d.get(val, a.get(def_key)) 已经足够好了。 - Alex Hall
你是不是想说 a.get 而不是 d.get...? - user202729
3
你可以创建一个dict的子类,并添加这样一个方法。 - Jean-François Fabre
你应该考虑以下问题:(1) 你的键值映射是否是常量?(2) 你的字典是否很大/占用内存很多/无法复制?如果答案分别为是和否,那么请考虑我的解决方案,否则Jean-Francois有一个出色的解决方案。 - jpp
3个回答

2

你可以继承 dict

class MyDict(dict):
    def get2(self,*keys):
        for k in keys:
            if k in self:
                return self.get(k)
        return None  # if not found

a = {1:2, 3:4, 5:6}
b = MyDict(a)
print(b.get2(2,10,5))

位置参数允许将行为扩展到n个键。一般情况下不能使用get方法来知道字典中是否有某个键,因为一些值可能是None,因此需要使用in测试。
使用一个哨兵对象避免重复字典测试。
class MyDict(dict):
    __notfound = object()
    def get2(self,*keys):
        for k in keys:
            x = self.get(k,self.__notfound )
            if x is not self.__notfound :
               return x
        return None  # if not found

我删除了我的回答,因为你的更好,但是关于一个值可能是“None”的事实,你可以使用一个哨兵对象来处理这种情况。 - Olivier Melançon

1

可以设置一个常量回退值。一种方法是使用 collections.defaultdict

请注意,这需要创建一个新字典。当然,我们可以将其分配给同一个变量。

from collections import defaultdict

a = {1:2, 3:4, 5:6}

a = defaultdict(lambda: a[5], a)

这将默认值设置为常量6,当找不到键时将返回该值。如果需要,每次更新a[5]时都必须重新设置默认值。

错误,如果我想要更新原始的 a 呢?这会创建另一个 a。第一个就 "丢失" 了。 - Jean-François Fabre
@Jean-FrançoisFabre,没错,我会加上免责声明的。 - jpp

1
你可以定义一个字典的子类来访问字典条目,并使用虚拟默认值来应用于任何不存在的键,但在引用它们时不会实际创建任何键(与setdefault()函数相反)。
 class dictWithDefault(dict):
     def __init__(self,aDict={},defaultValue=None):
         super().__init__(aDict)
         def aFunction():pass
         self._default = defaultValue if type(defaultValue) == type(aFunction) else lambda : defaultValue
     def __getitem__(self,key):
         return super().__getitem__(key) if key in self else self._default()

 d = {1:2, 3:4, 5:6}

 d = dictWithDefault(d,99)
 d[1] # >>> 2
 d[7] # >>> 99  this is the default value, but key 7 still doesn't exist
 d[7] = 97
 d[7] # >>> 97 once assigned the key has its own value
 d._default = 100 # you can change the virtual default at any time
 d[9] # >>> 100
 d[8] += 5   # this can be useful when key/values are used to count things
             # (using a default value of zero) akin to a Bag structure
 d[8] # >>> 105
 d    # >>> {1: 2, 3: 4, 5: 6, 7: 97, 8: 105}

你可以直接创建一个带有默认值的字典:
d = dictWithDefault({1:2, 3:4, 5:6},99)

如果想要使用默认键而不是默认值,您可以使用相同的技术,只需更改getitem方法的实现。

或者,您可以简单地使用d = dictWithDefault(d)而不使用默认值,并使用or运算符来获取替代键:

d = dictWithDefault(d)
value = d[7] or d[5]

[编辑] 更改了代码以支持对象作为默认值。

当在字典中使用对象(例如列表)作为值时,您必须小心。在第一次隐式赋值给新键时,它将仅被赋予对 _default 对象的引用。这意味着所有这些键最终都会引用相同的对象。

例如:d = dictWithDefault(defaultValue=[]) 将不能按预期工作。

d = dictWithDefault(defaultValue=[])

d["A"].append(1)
d["B"].append(2)

# will end up with { "A":[1,2], "A":[1,2] } 
# because both keys (A and B) reference the same list instance.

为了解决这个问题,我改变了函数的定义方式,使其可以接受一个lambda表达式:在使用对象作为默认值的情况下。
这样可以使默认值在用于新键时创建一个新实例。
d = dictWithDefault(defaultValue=lambda:[])

d["A"].append(1)
d["B"].append(2)

# now works properly giving: { "A":[1], "B":[2] }

对于简单类型(如字符串、整数等),您仍然可以在没有lambda的情况下使用该类,但是必须确保在字典将对象存储为值时使用实例创建方法与lambda一起使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接