一个类有一个构造函数,它接受一个参数:
class C(object):
def __init__(self, v):
self.v = v
...
在代码中的某处,dict 中的值知道它们的键很有用。
我想使用 defaultdict 与传递给新生儿默认值的键:
d = defaultdict(lambda : C(here_i_wish_the_key_to_be))
有什么建议么?
一个类有一个构造函数,它接受一个参数:
class C(object):
def __init__(self, v):
self.v = v
...
在代码中的某处,dict 中的值知道它们的键很有用。
我想使用 defaultdict 与传递给新生儿默认值的键:
d = defaultdict(lambda : C(here_i_wish_the_key_to_be))
有什么建议么?
它几乎算不上聪明——但子类化是你的朋友:
class keydefaultdict(defaultdict):
def __missing__(self, key):
if self.default_factory is None:
raise KeyError( key )
else:
ret = self[key] = self.default_factory(key)
return ret
d = keydefaultdict(C)
d[x] # returns C(x)
不,那里没有。
defaultdict
无法将实现配置为将缺失传递给key
开箱default_factory
即用。defaultdict
正如上面@JochenRitzel 所建议的那样,您唯一的选择是实现您自己的子类。
但这并不像标准库解决方案那样“聪明”或几乎像标准库解决方案那样干净(如果它存在的话)。因此,您简洁的是/否问题的答案显然是“否”。
标准库缺少这样一个经常需要的工具太糟糕了。
我认为你根本不需要defaultdict
这里。为什么不直接使用dict.setdefault
方法?
>>> d = {}
>>> d.setdefault('p', C('p')).v
'p'
这当然会创建许多C
. 如果这是一个问题,我认为更简单的方法可以做到:
>>> d = {}
>>> if 'e' not in d: d['e'] = C('e')
据我所知,它会比 thedefaultdict
或任何其他替代方案更快。
关于in
测试速度与使用 try-except 子句的ETA :
>>> def g():
d = {}
if 'a' in d:
return d['a']
>>> timeit.timeit(g)
0.19638929363557622
>>> def f():
d = {}
try:
return d['a']
except KeyError:
return
>>> timeit.timeit(f)
0.6167065411074759
>>> def k():
d = {'a': 2}
if 'a' in d:
return d['a']
>>> timeit.timeit(k)
0.30074866358404506
>>> def p():
d = {'a': 2}
try:
return d['a']
except KeyError:
return
>>> timeit.timeit(p)
0.28588609450770264
这是一个自动添加值的字典的工作示例。在 /usr/include 中查找重复文件的演示任务。注意自定义字典PathDict只需要四行:
class FullPaths:
def __init__(self,filename):
self.filename = filename
self.paths = set()
def record_path(self,path):
self.paths.add(path)
class PathDict(dict):
def __missing__(self, key):
ret = self[key] = FullPaths(key)
return ret
if __name__ == "__main__":
pathdict = PathDict()
for root, _, files in os.walk('/usr/include'):
for f in files:
path = os.path.join(root,f)
pathdict[f].record_path(path)
for fullpath in pathdict.values():
if len(fullpath.paths) > 1:
print("{} located in {}".format(fullpath.filename,','.join(fullpath.paths)))
另一种可能实现所需功能的方法是使用装饰器
def initializer(cls: type):
def argument_wrapper(
*args: Tuple[Any], **kwargs: Dict[str, Any]
) -> Callable[[], 'X']:
def wrapper():
return cls(*args, **kwargs)
return wrapper
return argument_wrapper
@initializer
class X:
def __init__(self, *, some_key: int, foo: int = 10, bar: int = 20) -> None:
self._some_key = some_key
self._foo = foo
self._bar = bar
@property
def key(self) -> int:
return self._some_key
@property
def foo(self) -> int:
return self._foo
@property
def bar(self) -> int:
return self._bar
def __str__(self) -> str:
return f'[Key: {self.key}, Foo: {self.foo}, Bar: {self.bar}]'
然后你可以这样创建defaultdict
:
>>> d = defaultdict(X(some_key=10, foo=15, bar=20))
>>> d['baz']
[Key: 10, Foo: 15, Bar: 20]
>>> d['qux']
[Key: 10, Foo: 15, Bar: 20]
将default_factory
创建X
具有指定参数的新实例。
当然,这只有在您知道该类将用于default_factory
. 否则,为了实例化一个单独的类,您需要执行以下操作:
x = X(some_key=10, foo=15)()
这有点难看......但是,如果您想避免这种情况并引入一定程度的复杂性,您还可以添加一个关键字参数,例如factory
whichargument_wrapper
将允许通用行为:
def initializer(cls: type):
def argument_wrapper(
*args: Tuple[Any], factory: bool = False, **kwargs: Dict[str, Any]
) -> Callable[[], 'X']:
def wrapper():
return cls(*args, **kwargs)
if factory:
return wrapper
return cls(*args, **kwargs)
return argument_wrapper
然后您可以在哪里使用该类:
>>> X(some_key=10, foo=15)
[Key: 10, Foo: 15, Bar: 20]
>>> d = defaultdict(X(some_key=15, foo=15, bar=25, factory=True))
>>> d['baz']
[Key: 15, Foo: 15, Bar: 25]