在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
)?
在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
)?
Answers:
other = s or "some default value"
好的,必须弄清楚or
操作员的工作方式。它是一个布尔运算符,因此可以在布尔上下文中工作。如果值不是布尔值,则出于运算符的目的,它们将转换为布尔值。
请注意,or
运算符不会仅返回True
或False
。相反,如果第一个操作数的计算结果为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运算符。
s
是有效值但不真实,会中断吗?(我不了解Python,所以我不确定'truthy'的概念是否适用。)
None
除了常量之外,数字0 和空容器(包括字符串)也被认为是假的False
。大多数其他一切都被认为是真实的。我要说的是,这里的主要危险是您将获得真实但非字符串的值,但这在某些程序中不会成为问题。
datetime.time(0)
也很虚假!
这是一个将返回第一个参数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
def coalesce(*arg): return next((a for a in arg if a is not None), None)
与上一行示例相同。
Consider x()?.y()?.z()
我知道已经解决了,但是在处理对象时还有另一种选择。
如果您的对象可能是:
{
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的对象
getattr()
。
get
如果值是None,则on dict不使用默认参数,但由于键不在dict中,因此不存在默认值时使用默认参数。结果{'a': None}.get('a', 'I do not want None')
仍然会给您None
。
除了朱利诺(Juliano)有关“或”行为的答案:“快速”
>>> 1 or 5/0
1
因此有时对于某些事情,它可能是一个有用的捷径
object = getCachedVersion() or getFromDB()
如果您需要嵌套多个null合并操作,例如:
model?.data()?.first()
这不是用容易解决的问题or
。它也不能解决.get()
需要字典类型或类似类型(无论如何都不能嵌套)的问题,或者getattr()
当NoneType没有属性时会抛出异常。
考虑向该语言添加空合并的相关点是PEP 505,与该文档相关的讨论在python-ideas线程中。
关于@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)
对于像我这样偶然发现此问题的可行解决方案的人,当变量可能未定义时,我得到的最接近的是:
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')
请注意,在检查全局变量时需要一个字符串,但是之后在检查值时将使用实际变量。
有关变量存在的更多信息: 如何检查变量是否存在?
(variablename or False) == True
与variablename == True
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。
希望这可以帮助
我发现下面的两个功能在处理许多可变测试用例时非常有用。
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
??
操作被提议为PEP 505。