是否可以使用lambda作为字典默认值?

时间:2012-02-20 19:32:27

标签: python

我正在尝试保留一个打开文件的字典,以便将数据拆分为单个文件。当我从字典中请求文件时,如果密钥不存在,我希望它被打开。但是,看起来我不能使用lambda作为默认值。

e.g。

files = {}
for row in data:
  f = files.get(row.field1, lambda: open(row.field1, 'w'))
  f.write('stuff...')

这不起作用,因为f设置为函数,而不是结果。使用上述语法的setdefault也不起作用。除此之外我还能做些什么:

f = files.get(row.field1)
if not f:
    f = files[row.field1] = open(row.field1, 'w')

5 个答案:

答案 0 :(得分:7)

这个用例对于defaultdict而言过于复杂,这就是为什么我不相信Python stdlib中存在这样的东西。但是,您可以轻松地自己编写一个通用的“扩展”defaultdict,将缺少的密钥传递给回调:

from collections import defaultdict

class BetterDefaultDict(defaultdict):
  def __missing__(self, key):
    return self.setdefault(key, self.default_factory(key))

用法:

>>> files = BetterDefaultDict(lambda key: open(key, 'w'))
>>> files['/tmp/test.py']
<open file '/tmp/test.py', mode 'w' at 0x7ff552ad6db0>

这适用于Python 2.7+,不了解旧版本:)另外,不要忘记再次关闭这些文件:

finally:
  for f in files.values(): f.close()

答案 1 :(得分:2)

你可以很容易地将get-and-open包装在类对象的__getitem__()中 - 例如:

class FileCache(object):
    def __init__(self):
        self.map = {}

    def __getitem__(self,key):
        if key not in self.map:            
            self.map[key] = open(key,'w')
        return self.map.key

答案 2 :(得分:1)

应该做你需要的子类的另一个选择:

class LambdaDefaultDict(dict):

    def get(self, key, default=None):
        try:
            return self[key]
        except KeyError:
            return default()

    def setdefault(self, key, default=None):
        if not self.has_key(key):
            self[key] = default() if default else None
        return self[key]

或者,或许更一般 - 允许默认值为值或可调用表达式:

class CallableDefaultDict(dict):

    def get(self, key, default=None):
        try:
            return self[key]
        except KeyError:
            return default() if callable(default) else default

    def setdefault(self, key, default=None):
        if not self.has_key(key):
            self[key] = default() if callable(default) else default
        return self[key]

答案 3 :(得分:1)

您可以使用集合模块中的defaultdict

class FileCache(collections.defaultdict):
  def __missing__(self, key):
    fo = open(key, 'w')
    self[key] = fo
    return fo

虽然做起来可能更好

files = {}
def get_file(f):
  fo = files.get(f)
  if fo is None:
    fo = open(f, 'w')
    files[f] = fo
  return fo

for row in data:
  f = get_file(row.field1)
  f.write('stuff...')

答案 4 :(得分:1)

这就是dict[key]语法引发KeyError

的确切原因
files = {}
for row in data:
  f = files.get(row.field1, lambda: open(row.field1, 'w'))
  f.write('stuff...')

应该成为:

files = {}
for row in data:
  try: f = files[row.field1]
  except KeyError: f = open(row.field1, 'w')
  f.write('stuff...')