簡體   English   中英

可作為 dict.get 的默認參數調用,如果密鑰存在則不調用它

[英]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() 方法返回一個指針的答案和評論中的討論。 你必須把它分成兩步。

您的選擇是:

  1. 如果您始終希望將該值作為默認值,並希望將其存儲在dict ,請使用帶有可調用對象的defaultdict

  2. 使用條件表達式:

     item = test['store'] if 'store' in test else run()
  3. 使用try / except

     try: item = test['store'] except KeyError: item = run()
  4. 使用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.pygeneral.pygeom.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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM