Reputation: 7821
I'm trying to keep a dictionary of open files for splitting data into individual files. When I request a file from the dictionary I would like it to be opened if the key isn't there. However, it doesn't look like I can use a lambda as a default.
e.g.
files = {}
for row in data:
f = files.get(row.field1, lambda: open(row.field1, 'w'))
f.write('stuff...')
This doesn't work because f is set to the function, rather than it's result. setdefault using the syntax above doesn't work either. Is there anything I can do besides this:
f = files.get(row.field1)
if not f:
f = files[row.field1] = open(row.field1, 'w')
Upvotes: 5
Views: 627
Reputation: 49856
This is the exact reason why dict[key]
syntax raises KeyError
:
files = {}
for row in data:
f = files.get(row.field1, lambda: open(row.field1, 'w'))
f.write('stuff...')
should become:
files = {}
for row in data:
try: f = files[row.field1]
except KeyError: f = open(row.field1, 'w')
f.write('stuff...')
Upvotes: 1
Reputation: 95328
This use case is too complex for a defaultdict
, which is why I don't believe that something like this exists in the Python stdlib. You can however easily write a generic "extended" defaultdict
yourself, which passes the missing key to the callback:
from collections import defaultdict
class BetterDefaultDict(defaultdict):
def __missing__(self, key):
return self.setdefault(key, self.default_factory(key))
Usage:
>>> files = BetterDefaultDict(lambda key: open(key, 'w'))
>>> files['/tmp/test.py']
<open file '/tmp/test.py', mode 'w' at 0x7ff552ad6db0>
This works in Python 2.7+, don't know about older versions :) Also, don't forget to close those files again:
finally:
for f in files.values(): f.close()
Upvotes: 7
Reputation: 36051
You can use defaultdict from the collections module
class FileCache(collections.defaultdict):
def __missing__(self, key):
fo = open(key, 'w')
self[key] = fo
return fo
Though it might be better to just do
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...')
Upvotes: 1
Reputation: 23346
Another option for a subclass that should do what you need:
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]
Or, perhaps more general - to allow defaults that are values or callable expressions:
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]
Upvotes: 1
Reputation: 19047
You could wrap the get-and-open in a class object's __getitem__()
pretty easily - something like:
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
Upvotes: 2