簡體   English   中英

dict.get(None) 在具有 None 值的 dict 上采用默認 arg

[英]dict.get(None) on a dict with None value takes default arg

我試圖概括這種情況:

def func(t):
    # code which doesn't accept None's

def foo(a):
    if a in ('', None):
        return None
    return func(a)

def foo_b(a):
    if a in ('', None):
        return None
    elif a == 'b'
         return 0
    return func(a)

我試圖解決它的方法是定義一個字典:

defaults = {None: None, '': None}

def foo(a):
    return defaults.get(a, func(a))

def foo_b(a):
    defaults_b = {'bar': 0, **defaults}
    return defaults_b.get(a, func(a))

運行腳本時:

foo_b(None)

它到達get方法,從defaults字典中獲取None並使用None進入func()方法,我得到錯誤,因為func()方法不排除None

用python解決這個問題的最佳實踐是什么?

這是行不通的,因為函數的所有參數都在調用函數之前進行了評估。 所以當你寫

defaults.get(a, func(a))

它首先評估afunc(a) ,並將兩個結果傳遞給defaults.get() 由於func(a)a某些值不起作用,因此在進入defaults.get()之前會出現錯誤。

一般解決這個問題的方法是默認為一個函數,它只會在必要時被調用。 您需要編寫自己的以這種方式工作的dict.get()版本。

def dict_get_f(d, key, default_fun):
    if key in d:
        return d[key]
    return default_fun()

然后你可以像這樣使用它:

def foo(a):
    return dict_get_f(defaults, a, lambda: func(a))

為什么不直接檢查函數本身?

def func(t):
    if t is None:
        return None
    # go on with the code

    

問題

正如@Barmar 在他們的回答中指出的那樣,問題不在於dict.get的行為,而是在調用函數之前評估所有參數的事實,因此defaults.get(a, func(a))調用func(a)在它使用該值調用defaults.get之前。

解決方案 1: collections.defaultdict

解決此問題的另一種方法是使用collections.defaultdict ,它提供了一種在值不在字典中時調用函數的方法。

由於您的字典很小,復制它們的成本很低,所以這個解決方案就是這樣做的。

from collections import defaultdict

defaults = {None: None, '': None}


def func(arg):
    """Some function that cannot be called with None"""
    if arg is None:
        raise ValueError()
    print(f'func called with {arg}')


def foo(a):
    return defaultdict(lambda: func(a), defaults)[a]


def foo_b(a):
    defaults_b = {'bar': 0, **defaults}
    return defaultdict(lambda: func(a), defaults_b)[a]

if __name__ == '__main__':
    print(foo(None))
    print(foo(''))
    print(foo(123))
    print(foo(123))
    print(foo_b(None))
    print(foo_b('bar'))

解決方案2:無處不在的功能!

您可以采用的另一種方法是將defaults字典設為函數字典,並使用lambda: func(a)的默認值lambda: func(a) ,以避免使用None調用函數。 因此, defaults.get(a, lambda: func(a))將返回一個返回正確值的函數,然后您可以調用該函數來獲取您想要的值: defaults.get(a, lambda: func(a))() .

一個完整的程序:

defaults = {None: (lambda: None), '': (lambda: None)}

def func(arg):
    """Some function that cannot be called with None"""
    if arg is None:
        raise ValueError()
    print(f'func called with {arg}')


def foo(a):
    return defaults.get(a, lambda: func(a))()


def foo_b(a):
    defaults_b = {'bar': (lambda: 0), **defaults}
    return defaults_b.get(a, lambda: func(a))()


if __name__ == '__main__':
    print(foo(None))
    print(foo(''))
    print(foo(123))
    print(foo(123))
    print(foo_b(None))
    print(foo_b('bar'))

解決方案 3:華麗且有些混淆的布爾運算短路

作為第三種選擇,您可以使用 Python 中的and行為,回答您列出的所有默認值都是“假”這一事實( 什么是真假?它與真假有什么不同? )。 在python中, a and b是短路的,這意味着如果aFalse或 falsy ,那么它已經知道a and b不能為True ,因此它返回a ,或者如果a為真則返回b

因此,如果aNone ,則defaults.get(a, True) and func(a)等於None and func(a) None是假的,所以None and func(a)等於None 如果a不在defaultsdefaults.get(a, True) and func(a)等於True and func(a) ,它等於func(a) ,因此該函數被調用。

請注意,如果您使用真實的默認值,這將不起作用,例如 if defaults = {None: None, '': None, 'bar': 'something'} ,並且可能難以理解可維護性 - 我只是想我會給另一個選擇。

defaults = {None: None, '': None}


def func(arg):
    """Some function that cannot be called with None"""
    if arg is None:
        raise ValueError()
    print(f'func called with {arg}')


def foo(a):
    return defaults.get(a, True) and func(a)


def foo_b(a):
    defaults_b = {'bar': 0, **defaults}
    return defaults_b.get(a, True) and func(a)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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