[英]Callable as the default argument to dict.get without it being called if the key exists
我正在嘗試提供 function 作為字典獲取 function 的默認參數,就像這樣
def run():
print "RUNNING"
test = {'store':1}
test.get('store', run())
但是,當它運行時,它會顯示以下 output:
RUNNING
1
所以我的問題是,正如標題所說,有沒有辦法提供一個可調用的作為 get 方法的默認值,而不用在密鑰存在時調用它?
另一種選擇,假設您不打算在字典中存儲虛假值:
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
請參閱dict.get() 方法返回一個指針的答案和評論中的討論。 你必須把它分成兩步。
您的選擇是:
如果您始終希望將該值作為默認值,並希望將其存儲在dict
,請使用帶有可調用對象的defaultdict
。
使用條件表達式:
item = test['store'] if 'store' in test else run()
使用try
/ except
:
try: item = test['store'] except KeyError: item = run()
使用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
具有一些非標准行為,那么子類化才真正有意義; 如果您通常希望它表現得像一個普通的dict
並且只想在一個地方懶惰地get
,請使用我的方法 2-4 之一。
我想您只想在密鑰不存在時才應用可調用對象。
有幾種方法可以做到這一點。 一種是使用 defaultdict,如果缺少 key,它會調用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
另一個相當丑陋的方法是只在 dict 中保存返回適當值的可調用對象。
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
如果您不想將此值添加到下一次調用的 dict 中,您有一個
def __missing__(self, key): return self.default_factory(key)
相反,每次未明確添加key: value
對時都會調用默認工廠。
如果您只知道可調用對象在他獲取呼叫站點上可能是什么,您可以將 dict 子類化為這樣的
class MyDict(dict):
def get_callable(self,key,func,*args,**kwargs):
'''Like ordinary get but uses a callable to
generate the default value'''
if key not in self:
val = func(*args,**kwargs)
else:
val = self[key]
return val
然后可以像這樣使用:-
>>> d = MyDict()
>>> d.get_callable(1,complex,2,3)
(2+3j)
>>> d[1] = 2
>>> d.get_callable(1,complex,2,3)
2
>>> def run(): print "run"
>>> repr(d.get_callable(1,run))
'2'
>>> repr(d.get_callable(2,run))
run
'None'
當可調用對象的計算成本很高時,這可能最有用。
我的項目中有一個util
目錄,其中包含qt.py
、 general.py
、 geom.py
等。在general.py
我有一堆 Python 工具,例如您需要的工具:
# Use whenever you need a lambda default
def dictGet(dict_, key, default):
if key not in dict_:
return default()
return dict_[key]
添加*args, **kwargs
如果您想支持使用不同的 args 多次調用默認值:
def dictGet(dict_, key, default, *args, **kwargs):
if key not in dict_:
return default(*args, **kwargs)
return dict_[key]
這是我使用的:
def lazy_get(d, k, f):
return d[k] if k in d else f(k)
后備 function f
將密鑰作為參數,例如
lazy_get({'a': 13}, 'a', lambda k: k) # --> 13
lazy_get({'a': 13}, 'b', lambda k: k) # --> 'b'
您顯然會使用更有意義的后備 function,但這說明了lazy_get
的靈活性。
這是帶有類型注釋的 function 的樣子:
from typing import Callable, Mapping, TypeVar
K = TypeVar('K')
V = TypeVar('V')
def lazy_get(d: Mapping[K, V], k: K, f: Callable[[K], V]) -> V:
return d[k] if k in d else f(k)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.