在c#中,有一个空合并操作符(写为??),允许在赋值时简单(短)检查空值:

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

python中有等效的吗?

我知道我能做到:

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

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


当前回答

other = s or "some default value"

好的,必须阐明or运算符是如何工作的。它是一个布尔运算符,所以它在布尔上下文中工作。如果值不是布尔值,则为运算符的目的将它们转换为布尔值。

注意,or操作符不仅返回True或False。相反,如果第一个操作数为真,它返回第一个操作数,如果第一个操作数为假,它返回第二个操作数。

在这种情况下,表达式x或y如果为True或在转换为布尔值时求值为True则返回x。否则返回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(只要你的类没有定义成员__nonzero__()和__len__()),使用与空合并操作符相同的语义是安全的。

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

在某些语言中,这种行为被称为Elvis操作符。

其他回答

如果你需要链接多个空条件操作,例如:

. data()模型?当代()

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

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

other = s or "some default value"

好的,必须阐明or运算符是如何工作的。它是一个布尔运算符,所以它在布尔上下文中工作。如果值不是布尔值,则为运算符的目的将它们转换为布尔值。

注意,or操作符不仅返回True或False。相反,如果第一个操作数为真,它返回第一个操作数,如果第一个操作数为假,它返回第二个操作数。

在这种情况下,表达式x或y如果为True或在转换为布尔值时求值为True则返回x。否则返回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(只要你的类没有定义成员__nonzero__()和__len__()),使用与空合并操作符相同的语义是安全的。

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

在某些语言中,这种行为被称为Elvis操作符。

关于@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不同,尽管它可以部分处理这种情况。它不能接受可迭代对象和默认值。最后一个参数将是返回的默认值,但在这种情况下,你不会传入一个可迭代对象,而且最后一个参数是默认值也不是显式的。

然后您可以执行下面的操作,但对于单值用例,我仍然使用not_null。

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

除了单个值的@Bothwells answer(我更喜欢这个),为了检查函数返回值的空值分配,你可以使用new walrus-operator(自python3.8以来):

def test():
    return

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

因此,测试函数不需要计算两次(如果test()为None else test(),则a = 2)

严格来说,

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

否则,s = False将成为“默认值”,这可能不是预期的。

如果你想让这段话更短,试试:

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

other = notNone(s, "default value")