是否有 C# 空合并运算符的 Python 等效项?
Posted
技术标签:
【中文标题】是否有 C# 空合并运算符的 Python 等效项?【英文标题】:Is there a Python equivalent of the C# null-coalescing operator? 【发布时间】:2011-06-26 02:39:32 【问题描述】:在 C# 中有一个 null-coalescing operator(写成 ??
),可以在赋值期间轻松(短)检查空值:
string s = null;
var other = s ?? "some default value";
有没有对应的python?
我知道我能做到:
s = None
other = s if s else "some default value"
但是有没有更短的方法(我不需要重复s
)?
【问题讨论】:
??
运算符建议为PEP 505。
..但从未进入语言。
Python 的最大优势之一是它的表现力。遗憾的是 Python 没有提供 None
-coalescing 运算符。三元替代方案更加冗长,or
解决方案根本不一样(因为它处理所有“虚假”值,而不仅仅是None
- 这并不总是你想要的,而且更容易出错) .
【参考方案1】:
other = s or "some default value"
好的,必须弄清楚or
运算符是如何工作的。它是一个布尔运算符,因此它在布尔上下文中工作。如果值不是布尔值,则将它们转换为布尔值以供运算符使用。
请注意,or
运算符不仅返回 True
或 False
。相反,如果第一个操作数的计算结果为真,则返回第一个操作数,如果第一个操作数的计算结果为假,则返回第二个操作数。
在这种情况下,如果表达式x or y
为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__()
),它就是安全地使用与 null-coalescing 运算符相同的语义。
事实上,Python 的这种副作用甚至可能很有用。由于您知道哪些值的计算结果为 false,因此您可以使用它来触发默认值,而无需专门使用 None
(例如错误对象)。
在某些语言中,这种行为称为Elvis operator。
【讨论】:
数字 0、None
和空容器(包括字符串)被认为是 false,除了常量 False
。大多数其他一切都被认为是真实的。我想说这里的主要危险是你会得到一个真实但非字符串的值,但这在某些程序中不是问题。
如果 s 为 None 或 False,则使用此 other 将获得默认值,这可能不是我们想要的。
还有很多由此引起的隐晦错误。例如在 Python 3.5 之前,datetime.time(0)
也是虚假的!
这很糟糕。我建议添加一个关于它的陷阱的通知。并建议不要使用它。
⚠️ 这是一个反模式。使用other = "some default value" if s is None else s
。另见:***.com/questions/13710631/…【参考方案2】:
严格来说,
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")
【讨论】:
Consider x()?.y()?.z()
这应该是答案
不知道如果我的情况是other = s.name if s is not None else "default value"
怎么办,还有像notNone
这样更短的方法吗?【参考方案3】:
这是一个函数,它将返回不是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)
与您在一行中的最后一个示例相同。
我知道人们想解释 if else sytnax 等,但 coalesce 采用任意参数列表,所以这应该是最佳答案。
glglgl 有最好的答案。我在一个大型测试数组上使用了 timeit,reduce 实现慢得令人无法接受,多行 for/if 版本最快,下一个实现稍微落后。考虑到简单性和简洁性,下一个版本总体上是最好的。
@glglgl 有有趣的 sn-p。不幸的是,因为 Python 没有按名称传递,所以像这样的合并不是短路的;在代码运行之前评估所有参数。
Consider x()?.y()?.z()
【参考方案4】:
如果您需要嵌套多个空合并操作,例如:
model?.data()?.first()
这不是or
容易解决的问题。它也不能用.get()
解决,它需要字典类型或类似类型(无论如何都不能嵌套)或getattr()
,当 NoneType 没有属性时会抛出异常。
考虑在语言中添加空值合并的相关 pip 是 PEP 505,与文档相关的讨论在 python-ideas 线程中。
【讨论】:
【参考方案5】:我知道这已经得到解答,但是当您处理类似 dict 的对象时,还有另一种选择。
如果你有一个对象可能是:
name:
first: "John",
last: "Doe"
你可以使用:
obj.get(property_name, value_if_null)
喜欢:
obj.get("name", ).get("first", "Name is missing")
通过添加作为默认值,如果缺少“name”,则返回一个空对象并传递给下一个get。这类似于 C# 中的 null-safe-navigation,类似于
obj?.name?.first
。
【讨论】:
并非所有对象都有.get
,这仅适用于类似字典的对象
我正在提交答案编辑以覆盖getattr()
。
get
on dict 如果值为 None 则不使用默认参数,但如果值不存在则使用默认参数,因为键不在 dict 中。因此,'a': None.get('a', 'I do not want None')
仍会为您提供None
。【参考方案6】:
除了 Juliano 关于“或”行为的回答: 它“快”
>>> 1 or 5/0
1
所以有时它可能是一个有用的捷径,比如
object = getCachedVersion() or getFromDB()
【讨论】:
您要查找的术语是“短路”。【参考方案7】:除了@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()
)
【讨论】:
【参考方案8】:关于@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)
【讨论】:
【参考方案9】:对于像我这样偶然发现此问题的可行解决方案的人,当变量可能未定义时,我得到的最接近的是:
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')
请注意,在检查全局变量时需要一个字符串,但在检查值时使用实际变量。
关于变量存在的更多信息: How do I check if a variable exists?
【讨论】:
(variablename or False) == True
与variablename == True
相同【参考方案10】:
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
如果您在字典中找不到名称,它将返回默认值, 如果名称存在,那么它将添加任何现有值与 1。
希望对你有帮助
【讨论】:
嗨,欢迎来到 Stack Overflow。您的答案添加了哪些现有答案尚未涵盖的新信息?例如,请参阅@Craig 的答案【参考方案11】:我发现下面的两个函数在处理许多可变测试用例时非常有用。
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
【讨论】:
这种东西添加了一大堆稍微不同的术语(例如“null”和“nz”在Python的上下文中都没有任何意义),从其他语言导入,加上变体(严格或非严格!)。这只会增加混乱。明确的“无”检查是您应该使用的。另外,当您使用函数调用时,您不会受益于操作员可以执行的任何快捷语义。以上是关于是否有 C# 空合并运算符的 Python 等效项?的主要内容,如果未能解决你的问题,请参考以下文章
C# 中是不是有 .isConnected 功能的 python 等效项