Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/362.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何实现惰性setdefault?_Python_Lazy Evaluation - Fatal编程技术网

Python 如何实现惰性setdefault?

Python 如何实现惰性setdefault?,python,lazy-evaluation,Python,Lazy Evaluation,dict.setdefault的一个小麻烦是,它总是计算第二个参数(当然是给定的),即使第一个参数已经是字典中的一个键 例如: import random def noisy_default(): ret = random.randint(0, 10000000) print 'noisy_default: returning %d' % ret return ret d = dict() print d.setdefault(1, noisy_default()) p

dict.setdefault
的一个小麻烦是,它总是计算第二个参数(当然是给定的),即使第一个参数已经是字典中的一个键

例如:

import random
def noisy_default():
    ret = random.randint(0, 10000000)
    print 'noisy_default: returning %d' % ret
    return ret

d = dict()
print d.setdefault(1, noisy_default())
print d.setdefault(1, noisy_default())
这将产生如下所示的ouptut:

noisy_default: returning 4063267
4063267
noisy_default: returning 628989
4063267
正如最后一行所确认的,第二次执行
noised\u default
是不必要的,因为此时键
1
已经出现在
d
中(值为
4063267

是否可以实现
dict
的子类,该子类的
setdefault
方法惰性地计算其第二个参数


编辑:

下面是一个由BrenBarn的评论和Pavel Anossov的回答启发而来的实现。在做这项工作时,我还实现了get的一个懒惰版本,因为其基本思想基本相同

class LazyDict(dict):
    def get(self, key, thunk=None):
        return (self[key] if key in self else
                thunk() if callable(thunk) else
                thunk)


    def setdefault(self, key, thunk=None):
        return (self[key] if key in self else
                dict.setdefault(self, key,
                                thunk() if callable(thunk) else
                                thunk))
现在,请看片段

d = LazyDict()
print d.setdefault(1, noisy_default)
print d.setdefault(1, noisy_default)
产生如下输出:

noisy_default: returning 5025427
5025427
5025427
请注意,上面
d.setdefault
的第二个参数现在是可调用的,而不是函数调用

LazyDict.get
LazyDict.setdefault
的第二个参数不可调用时,它们的行为方式与相应的
dict
方法相同

如果要将callable本身作为默认值传递(即,不打算调用),或者如果要调用的callable需要参数,请将
lambda:
前置到相应的参数。例如:

d1.setdefault('div', lambda: div_callback)

d2.setdefault('foo', lambda: bar('frobozz'))
那些不喜欢覆盖
get
setdefault
的想法,和/或因此需要测试可调用性等的人可以使用此版本:

class LazyButHonestDict(dict):
    def lazyget(self, key, thunk=lambda: None):
        return self[key] if key in self else thunk()


    def lazysetdefault(self, key, thunk=lambda: None):
        return (self[key] if key in self else
                self.setdefault(key, thunk()))

否,参数的计算在调用之前进行。您可以实现一个类似于
setdefault
的函数,该函数将callable作为其第二个参数,并仅在需要时调用它。

这也可以通过
defaultdict
实现。它用一个可调用的元素实例化,然后在访问不存在的元素时调用该元素

from collections import defaultdict

d = defaultdict(noisy_default)
d[1] # noise
d[1] # no noise
defaultdict
的警告是,可调用对象不获取任何参数,因此不能像使用
dict.setdefault
那样从键派生默认值。可以通过重写子类中的
\uuuuu missing\uuuuu
来缓解此问题:

from collections import defaultdict

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

def noisy_default_with_key(key):
    print key
    return key + 1

d = defaultdict2(noisy_default_with_key)
d[1] # prints 1, sets 2, returns 2
d[1] # does not print anything, does not set anything, returns 2

有关更多信息,请参阅模块。

您可以使用三元运算符在一行中执行此操作:

value = cache[key] if key in cache else cache.setdefault(key, func(key))
如果您确信
缓存
永远不会存储错误值,则可以将其简化一点:

value = cache.get(key) or cache.setdefault(key, func(key))

似乎没有一个班轮不需要额外的类或额外的查找。为了记录在案,这里有一个简单(甚至不简洁)的方法来实现这一点,而不需要它们中的任何一个

try:
    value = dct[key]
except KeyError:
    value = noisy_default()
    dct[key] = value
return value

你不能让它不计算第二个参数。您需要做的是将该参数包装在一个函数中(例如,使用
lambda
),然后让
setdefault
仅在需要时调用该函数。我建议您在
lazyget
lazysetdefault
的签名中添加
*args、**kwargs
,以及调用
thunk()
?这将允许懒惰的东西获取参数。e、 g.
lbd.lazysetdefault('total',sum,[1,2,3,4],start=2)
如果您正在检查dict中的
key
则使用
setdeault
没有意义,这将需要在
缓存中搜索
key
两次。这对于基于哈希映射的dict来说不是什么大问题,但仍然没有太大意义。@user1685095如果不调用setdefault,缓存将不会更新。setdefault是设置空缓存并同时返回其值,不查找密钥两次将是从O(“2”n)到O(n)的微观优化。如果这是一个问题,Python可能是错误的选择。