是否有Python等效于C#null-coalescing运算符?


301

在C#中,有一个null-coalescing运算符(写为??),允许在赋值期间轻松(简短)进行null检查:

string s = null;
var other = s ?? "some default value";

有python等效项吗?

我知道我可以做到:

s = None
other = s if s else "some default value"

但是,还有没有更短的方法(我不需要重复s)?


14
??操作被提议为PEP 505
200_success

Answers:


421
other = s or "some default value"

好的,必须弄清楚or操作员的工作方式。它是一个布尔运算符,因此可以在布尔上下文中工作。如果值不是布尔值,则出于运算符的目的,它们将转换为布尔值。

请注意,or运算符不会仅返回TrueFalse。相反,如果第一个操作数的计算结果为true,则返回第一个操作数;如果第一个操作数的计算结果为false,则返回第二个操作数。

在这种情况下,如果表达式为true ,则x or y返回x它,True或者当转换为boolean时,表达式为true。否则,返回y。在大多数情况下,这将与C♯的空值运算符相同,但请记住:

42    or "something"    # returns 42
0     or "something"    # returns "something"
None  or "something"    # returns "something"
False or "something"    # returns "something"
""    or "something"    # returns "something"

如果您使用变量s来保存对类实例的引用或None(只要您的类未定义member __nonzero__()__len__()),则使用与null-coalescing运算符相同的语义是安全的。

实际上,拥有Python的这种副作用甚至可能是有用的。由于您知道哪些值的计算结果为false,因此可以使用它来触发默认值,而无需None专门使用(例如,错误对象)。

在某些语言中,此行为称为Elvis运算符


3
这项工作会一样吗?我的意思是,如果s是有效值但不真实,会中断吗?(我不了解Python,所以我不确定'truthy'的概念是否适用。)
cHao 2011年

9
None除了常量之外,数字0 和空容器(包括字符串)也被认为是假的False。大多数其他一切都被认为是真实的。我要说的是,这里的主要危险是您将获得真实但非字符串的值,但这在某些程序中不会成为问题。
kindall 2011年

25
使用此将得到的默认值,如果s是无或假,这可能不是什么都想。
pafcu 2011年

6
也有许多由此引起的模糊错误。例如,在Python 3.5之前的版本datetime.time(0)也很虚假!
Antti Haapala

19
这是不好的。我建议添加有关其陷阱的通知。并建议不要使用它。
Mateen Ulhaq '18年

61

严格来说

other = s if s is not None else "default value"

否则,s = False将变为"default value",这可能不是预期的。

如果要缩短此时间,请尝试:

def notNone(s,d):
    if s is None:
        return d
    else:
        return s

other = notNone(s, "default value")

1
Consider x()?.y()?.z()
nurettin

40

这是一个将返回第一个参数not的函数None

def coalesce(*arg):
  return reduce(lambda x, y: x if x is not None else y, arg)

# Prints "banana"
print coalesce(None, "banana", "phone", None)

reduce()即使第一个参数不是None,也可能不必要地遍历所有参数,因此您也可以使用以下版本:

def coalesce(*arg):
  for el in arg:
    if el is not None:
      return el
  return None

23
def coalesce(*arg): return next((a for a in arg if a is not None), None)与上一行示例相同。
glglgl 2014年

2
我知道人们想解释sytnax等,但是结盟采用了一个任意的参数列表,因此这确实是最好的答案。
Eric Twilegar

2
glglgl是最好的答案。我在大型测试阵列上使用了timeit,reduce的实现速度慢得令人无法接受,for / if版本的多行速度最快,而下一个实现则略微落后。考虑到简单性和简洁性,下一个版本是最佳的总体选择。
粘土

3
@glglgl有有趣的摘要。不幸的是,由于Python没有名字传递,像这样的合并不会造成短路。在代码运行之前,将评估所有参数。
user1338062

Consider x()?.y()?.z()
nurettin

5

我知道已经解决了,但是在处理对象时还有另一种选择。

如果您的对象可能是:

{
   name: {
      first: "John",
      last: "Doe"
   }
}

您可以使用:

obj.get(property_name, value_if_null)

喜欢:

obj.get("name", {}).get("first", "Name is missing") 

通过添加{}作为默认值,如果缺少“名称”,则返回一个空对象并将其传递到下一个get。这类似于C#中的null-safe-navigation,就像obj?.name?.first


并非所有对象都有.get,这仅适用于类似dict的对象
timdiels

我正在提交答案编辑,以涵盖其他内容getattr()
dgw

get如果值是None,则on dict不使用默认参数,但由于键不在dict中,因此不存在默认值时使用默认参数。结果{'a': None}.get('a', 'I do not want None')仍然会给您None
Patrick Mevzek

3

除了朱利诺(Juliano)有关“或”行为的答案:“快速”

>>> 1 or 5/0
1

因此有时对于某些事情,它可能是一个有用的捷径

object = getCachedVersion() or getFromDB()

17
您要查找的术语是“短路”。
jpmc26 2014年

1

对于单个值,除了@Bothwells答案(我更喜欢)之外,为了对函数返回值进行空值检查,您可以使用新的walrus-operator(自python3.8起):

def test():
    return

a = 2 if (x:= test()) is None else x

因此,test无需对函数进行两次评估(如中的所示a = 2 if test() is None else test()


1

如果您需要嵌套多个null合并操作,例如:

model?.data()?.first()

这不是用容易解决的问题or。它也不能解决.get()需要字典类型或类似类型(无论如何都不能嵌套)的问题,或者getattr()当NoneType没有属性时会抛出异常。

考虑向该语言添加空合并的相关点是PEP 505,与该文档相关的讨论在python-ideas线程中。


0

关于@Hugh Bothwell,@ mortehu和@glglgl的回答。

设置测试数据集

import random

dataset = [random.randint(0,15) if random.random() > .6 else None for i in range(1000)]

定义实施

def not_none(x, y=None):
    if x is None:
        return y
    return x

def coalesce1(*arg):
  return reduce(lambda x, y: x if x is not None else y, arg)

def coalesce2(*args):
    return next((i for i in args if i is not None), None)

使测试功能

def test_func(dataset, func):
    default = 1
    for i in dataset:
        func(i, default)

使用Python 2.7在Mac i7 @ 2.7Ghz上的结果

>>> %timeit test_func(dataset, not_none)
1000 loops, best of 3: 224 µs per loop

>>> %timeit test_func(dataset, coalesce1)
1000 loops, best of 3: 471 µs per loop

>>> %timeit test_func(dataset, coalesce2)
1000 loops, best of 3: 782 µs per loop

显然,该not_none函数可以正确回答OP的问题并处理“虚假”问题。它也是最快,最容易阅读的。如果将逻辑应用于很多地方,显然这是最好的方法。

如果您有一个问题想要在迭代中找到第一个非空值,那么@mortehu的响应就是解决方法。但这是与OP 不同的解决方案,尽管它可以部分解决这种情况。它不能采用可迭代的AND默认值。最后一个参数将是返回的默认值,但在这种情况下,您将不会传递可迭代的值,而且也不清楚最后一个参数是否是value的默认值。

然后,您可以在下面做,但我仍将使用not_null单值用例。

def coalesce(*args, **kwargs):
    default = kwargs.get('default')
    return next((a for a in arg if a is not None), default)

0

对于像我这样偶然发现此问题的可行解决方案的人,当变量可能未定义时,我得到的最接近的是:

if 'variablename' in globals() and ((variablename or False) == True):
  print('variable exists and it\'s true')
else:
  print('variable doesn\'t exist, or it\'s false')

请注意,在检查全局变量时需要一个字符串,但是之后在检查值时将使用实际变量。

有关变量存在的更多信息: 如何检查变量是否存在?


1
(variablename or False) == Truevariablename == True
mic

-2
Python has a get function that its very useful to return a value of an existent key, if the key exist;
if not it will return a default value.

def main():
    names = ['Jack','Maria','Betsy','James','Jack']
    names_repeated = dict()
    default_value = 0

    for find_name in names:
        names_repeated[find_name] = names_repeated.get(find_name, default_value) + 1

如果您在字典中找不到名称,它将返回default_value,如果名称存在,则将任何现有值加1。

希望这可以帮助


1
嗨,欢迎来到Stack Overflow。您的答案添加了哪些新信息,而现有答案尚未涵盖这些信息?例如,请参阅
@Craig

-6

我发现下面的两个功能在处理许多可变测试用例时非常有用。

def nz(value, none_value, strict=True):
    ''' This function is named after an old VBA function. It returns a default
        value if the passed in value is None. If strict is False it will
        treat an empty string as None as well.

        example:
        x = None
        nz(x,"hello")
        --> "hello"
        nz(x,"")
        --> ""
        y = ""   
        nz(y,"hello")
        --> ""
        nz(y,"hello", False)
        --> "hello" '''

    if value is None and strict:
        return_val = none_value
    elif strict and value is not None:
        return_val = value
    elif not strict and not is_not_null(value):
        return_val = none_value
    else:
        return_val = value
    return return_val 

def is_not_null(value):
    ''' test for None and empty string '''
    return value is not None and len(str(value)) > 0

5
这种事情增加了一大堆稍微不同的术语(例如“ null”和“ nz”,在Python的上下文中都不表示任何东西),这些术语是从其他语言中导入的,并带有变体(严格或不严格!)。这只会增加混乱。显式“为无”检查是您应该使用的检查。另外,您不会从操作员使用函数调用时可以使用的任何快捷语义中受益。
spookylukey
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.