可调用对象作为dict.get的默认参数,但如果键存在则不被调用

22 投票
6 回答
10311 浏览
提问于 2025-04-17 03:26

我想给字典的 get 函数提供一个默认参数,像这样:

def run():
   print "RUNNING"

test = {'store':1}
test.get('store', run())

但是当我运行这个代码时,输出结果是:

RUNNING
   1

所以我想问的是,标题所说的那样,有没有办法给 get 方法提供一个可以调用的默认值,而在键存在的情况下不让它被调用?

6 个回答

3

我想你是想在键不存在的时候才调用某个函数。

有几种方法可以做到这一点。第一种方法是使用一个叫做defaultdict的东西,它会在键缺失时自动调用run()函数。

from collections import defaultdict
def run():
   print "RUNNING"

test = {'store':1}
test.get('store', run())

test = defaultdict(run, store=1) # provides a value for store
test['store'] # gets 1
test['runthatstuff'] # gets None

另一种方法,虽然有点复杂,就是只在字典里保存那些能返回合适值的函数。

test = {'store': lambda:1}
test.get('store', run)() # -> 1
test.get('runrun', run)() # -> None, prints "RUNNING".

如果你想让返回值依赖于缺失的键,那你需要创建一个defaultdict的子类:

class mydefaultdict(defaultdict):
    def __missing__(self, key):
        val = self[key] = self.default_factory(key)
        return val

d = mydefaultdict(lambda k: k*k)
d[10] # yields 100

@mydefaultdict # decorators are fine
def d2(key):
    return -key
d2[5] # yields -5

如果你不想把这个值添加到字典里以便下次调用,你可以使用一个

def __missing__(self, key): return self.default_factory(key)

它会在每次key: value对没有被明确添加时调用默认工厂。

19

还有一个选择,前提是你不打算在字典里存储假值

test.get('store') or run()

在Python中,or运算符不会去计算那些不需要的参数(它会短路)


如果你需要支持假值,那么你可以使用get_or_run(test, 'store', run),其中:

def get_or_run(d, k, f):
    sentinel = object()  # guaranteed not to be in d
    v = d.get(k, sentinel)
    return f() if v is sentinel else v
10

可以查看dict.get()方法返回指针中的回答和评论进行讨论。你需要把这个问题分成两个步骤来解决。

你有以下几种选择:

  1. 如果你总是希望有一个默认值,并且想把它存储在字典里,可以使用一个带有可调用对象的defaultdict

  2. 使用条件表达式:

    item = test['store'] if 'store' in test else run()
    
  3. 使用try / except

    try:
        item = test['store']
    except KeyError:
        item = run()
    
  4. 使用get

    item = test.get('store')
    if item is None:
        item = run()
    

还有这些方法的变体。

glglgl展示了一种子类化defaultdict的方法,你也可以在某些情况下直接子类化dict

def run():
    print "RUNNING"
    return 1

class dict_nokeyerror(dict):
    def __missing__(self, key):
        return run()

test = dict_nokeyerror()

print test['a']
# RUNNING
# 1

只有在你总是希望字典有一些非标准行为时,子类化才真正有意义;如果你一般希望它像普通的dict那样工作,只是在某个地方想要懒惰的get,可以使用我提到的第二到第四种方法。

撰写回答