Python 中是否存在与 C# 空合并运算符等效的函数?

发布于 2024-10-17 03:23:37 字数 462 浏览 2 评论 0原文

在 C# 中,有一个 空合并运算符(写为 ? ?),允许在赋值期间进行简单(简短)的空检查:

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

是否有Python等效项?

我知道我可以这样做:

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

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

In C# there's a null-coalescing operator (written as ??) that allows for easy (short) null checking during assignment:

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

Is there a python equivalent?

I know that I can do:

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

But is there an even shorter way (where I don't need to repeat s)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(17

有木有妳兜一样 2024-10-24 03:23:38

我意识到这个问题已经得到解答,但是当您处理类似字典的对象时,还有另一种选择。

如果您有一个对象可能是:

{
   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

I realize this is answered, but there is another option when you're dealing with dict-like objects.

If you have an object that might be:

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

You can use:

obj.get(property_name, value_if_null)

Like:

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

By adding {} as the default value, if "name" is missing, an empty object is returned and passed through to the next get. This is similar to null-safe-navigation in C#, which would be like obj?.name?.first.

微凉 2024-10-24 03:23:38

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

def test():
    return

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

因此,test函数不需要计算两次(如 a = 2 if test() is None else test()

Addionally to @Bothwells answer (which I prefer) for single values, in order to null-checking assingment of function return values, you can use new walrus-operator (since python3.8):

def test():
    return

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

Thus, test function does not need to be evaluated two times (as in a = 2 if test() is None else test())

面如桃花 2024-10-24 03:23:38

除了 Juliano 关于“或”行为的回答之外:
它“快”

>>> 1 or 5/0
1

所以有时它可能是一个有用的快捷方式,例如

object = getCachedVersion() or getFromDB()

In addition to Juliano's answer about behavior of "or":
it's "fast"

>>> 1 or 5/0
1

So sometimes it's might be a useful shortcut for things like

object = getCachedVersion() or getFromDB()
莫多说 2024-10-24 03:23:38

处理可能的异常:

def default_val(expr, default=None):
    try:
        tmp = expr()
    except Exception:
        tmp = default
    return tmp

像这样使用它:

default_val(lambda: some['complex'].expression('with', 'possible')['exceptions'], '')

to take care of possible exceptions:

def default_val(expr, default=None):
    try:
        tmp = expr()
    except Exception:
        tmp = default
    return tmp

use it like that:

default_val(lambda: some['complex'].expression('with', 'possible')['exceptions'], '')
烟若柳尘 2024-10-24 03:23:38

对于 2024 年仍需要函数空合并的任何人:

def null_coalesce(*args):
    return getattr(args[0], args[1], None) if len(args) == 2 else null_coalesce(getattr(args[0], args[1], None), *args[2:])

对我有用。如果您想要访问可能存在或不存在的字段,则此变体适用。例如,对于 foo.bar._name_,您将执行 null_coalesce(foo, 'bar', '__name__') 并且会收到函数的名称,或者如果字段不存在,您将得到 None 和 NOT an AttributeError

For anyone still in need of a null coalescing for functions in 2024:

def null_coalesce(*args):
    return getattr(args[0], args[1], None) if len(args) == 2 else null_coalesce(getattr(args[0], args[1], None), *args[2:])

worked for me. This variant works if you want to access a field that may or may not exist. E.g. for foo.bar._name_ you would execute null_coalesce(foo, 'bar', '__name__') and would either receive the name of the function or if this field does not exist, you would get None and NOT an AttributeError.

怕倦 2024-10-24 03:23:38

关于 @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)

Regarding answers by @Hugh Bothwell, @mortehu and @glglgl.

Setup Dataset for testing

import random

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

Define implementations

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)

Make test function

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

Results on mac i7 @2.7Ghz using python 2.7

>>> %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

Clearly the not_none function answers the OP's question correctly and handles the "falsy" problem. It is also the fastest and easiest to read. If applying the logic in many places, it is clearly the best way to go.

If you have a problem where you want to find the 1st non-null value in a iterable, then @mortehu's response is the way to go. But it is a solution to a different problem than OP, although it can partially handle that case. It cannot take an iterable AND a default value. The last argument would be the default value returned, but then you wouldn't be passing in an iterable in that case as well as it isn't explicit that the last argument is a default to value.

You could then do below, but I'd still use not_null for the single value use case.

def coalesce(*args, **kwargs):
    default = kwargs.get('default')
    return next((a for a in arg if a is not None), default)
甜味拾荒者 2024-10-24 03:23:38

尝试一下 opytional

>>> import opytional as opyt
>>> opyt.or_value('value', "some default value")
'value'
>>> opyt.or_value(None, "some default value")
'some default value'
>>> opyt.or_value("", "some default value")  # handles falsey value safely
''

该库还提供了 or_elseapply_if 来安全、富有表现力地处理可能为 None 的值。可通过 pip、python3 -m pip install opytional 进行安装。

免责声明:我是图书馆作者

Give the opytional library a try.

>>> import opytional as opyt
>>> opyt.or_value('value', "some default value")
'value'
>>> opyt.or_value(None, "some default value")
'some default value'
>>> opyt.or_value("", "some default value")  # handles falsey value safely
''

The library also provides or_else and apply_if for safe, expressive handling of values that may be None. Installation is available via pip, python3 -m pip install opytional.

Disclaimer: am library author

↘紸啶 2024-10-24 03:23:38

我们可以这样尝试

 other = s if s != None else 0 // to pass as 0 
 other = s if s != None else 'Some value in case of Null' // pass text 

We can try like this

 other = s if s != None else 0 // to pass as 0 
 other = s if s != None else 'Some value in case of Null' // pass text 
[浮城] 2024-10-24 03:23:38

我遇到了类似的问题,但上面的解决方案都不能让我满意。我做了一些测试,然后我想到了一个好主意......而且它有效!

我认为根据 Python 标准,它既优雅又具有足够的“描述性”。我会让你来判断。

首先是问题:

我有一个类 Completion ,它引用了 sequence_type1sequence_type2

我想定义一个如下属性

@property
def seq_pk(self):
    return self.sequence_type1.seq_pk if self.sequence_type1 else self.sequence_type2.seq_pk
    

:看起来不太好看,占用大量空间......而且我需要对 Completion 的所有属性执行相同的操作!

我不能简单地这样做

return self.sequence_type1.seq_pk or self.sequence_type2.seq_pk

,因为如果 self.sequence_type1 不存在,则会引发异常,而不是给我 self.sequence_type2.seq_pk

但我发现效果很好的是以下:

return (self.sequence_type1 or self.sequence_type2).seq_pk

我认为简单、描述性、优雅

I had a similar issue, but none of the solution above were quite satisfactory for me. I did some tests, then a nice idea came to my mind ... and it works !

it is at the same time elegant and 'descriptive' enough according to Python standards I think. I'll let you the judge of it.

First the issue:

I have a class Completion which has either a reference to sequence_type1 or sequence_type2

I want to define a property such as this:

@property
def seq_pk(self):
    return self.sequence_type1.seq_pk if self.sequence_type1 else self.sequence_type2.seq_pk
    

That's not really nice looking, takes a lot of space ... and I will need to do the same for all the properties of Completion !

I cannot simply do

return self.sequence_type1.seq_pk or self.sequence_type2.seq_pk

because if self.sequence_type1 does not exist, an exception will be raised, instead of giving me self.sequence_type2.seq_pk

But what I discovered works nicely is the following:

return (self.sequence_type1 or self.sequence_type2).seq_pk

simple, descriptive, elegant I think

情痴 2024-10-24 03:23:38

我没有解释 or 运算符的作用,以及它为什么不是一个有用的空合并运算符,而是提出了一个实用的 coalesce 函数。

def coalesce(*args):
    '''
    Returns the first non-None argument,
    or None if all arguments are None.
    If an argument is a function,
    it is called and its result is used instead.
    '''
    for arg in args:
        if callable(arg):
            arg = arg()
        if arg is not None:
            return arg

验证它是否有效:

print(f'Should be None: { coalesce(None) }')
print(f'Should be 0: { coalesce(0) }')
print(f'Should be 1: { coalesce(None, 1) }')
print(f'Should be 0: { coalesce(0, 1) }')
print(f'Should be 1: { coalesce(None, lambda: None, 1) }')
print(f'Should be 1: { coalesce(None, lambda: 1) }')
print(f'Should be "": "{ coalesce("", 1) }"')
print(f'Should be False: { coalesce(False, 1) }')

为了提高性能,当计算值的成本昂贵时,可以提供 lambda 函数而不是常量,这样,如果早期参数在运行时不是 None ,就可以避免这种费用。

Rather than explain what the or operator does, and how it is not a useful null-coalescing operator, I propose a pragmatic coalesce function.

def coalesce(*args):
    '''
    Returns the first non-None argument,
    or None if all arguments are None.
    If an argument is a function,
    it is called and its result is used instead.
    '''
    for arg in args:
        if callable(arg):
            arg = arg()
        if arg is not None:
            return arg

Verify it works:

print(f'Should be None: { coalesce(None) }')
print(f'Should be 0: { coalesce(0) }')
print(f'Should be 1: { coalesce(None, 1) }')
print(f'Should be 0: { coalesce(0, 1) }')
print(f'Should be 1: { coalesce(None, lambda: None, 1) }')
print(f'Should be 1: { coalesce(None, lambda: 1) }')
print(f'Should be "": "{ coalesce("", 1) }"')
print(f'Should be False: { coalesce(False, 1) }')

To improve performance one would supply a lambda function instead of a constant when the value is expensive to compute, and that way the expense can be avoided if an earlier argument is not None at runtime.

凉月流沐 2024-10-24 03:23:38
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。

希望这可以有所帮助

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

if you cannot find the name inside the dictionary, it will return the default_value,
if the name exist then it will add any existing value with 1.

hope this can help

我的鱼塘能养鲲 2024-10-24 03:23:38

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

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')

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

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

For those like me that stumbled here looking for a viable solution to this issue, when the variable might be undefined, the closest i got is:

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')

Note that a string is needed when checking in globals, but afterwards the actual variable is used when checking for value.

More on variable existence:
How do I check if a variable exists?

能怎样 2024-10-24 03:23:38

我发现下面的两个函数在处理许多变量测试用例时非常有用。

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

The two functions below I have found to be very useful when dealing with many variable testing cases.

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
七度光 2024-10-24 03:23:37
other = s or "some default value"

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

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

在这种情况下,如果表达式 x 或 yTrue,则返回 x,或者在转换为布尔值时计算结果为 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(只要您的类未定义成员 __nonzero__()__len__()),就可以安全地使用与空合并运算符相同的语义。

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

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

other = s or "some default value"

Ok, it must be clarified how the or operator works. It is a boolean operator, so it works in a boolean context. If the values are not boolean, they are converted to boolean for the purposes of the operator.

Note that the or operator does not return only True or False. Instead, it returns the first operand if the first operand evaluates to true, and it returns the second operand if the first operand evaluates to false.

In this case, the expression x or y returns x if it is True or evaluates to true when converted to boolean. Otherwise, it returns y. For most cases, this will serve for the very same purpose of C♯'s null-coalescing operator, but keep in mind:

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

If you use your variable s to hold something that is either a reference to the instance of a class or None (as long as your class does not define members __nonzero__() and __len__()), it is secure to use the same semantics as the null-coalescing operator.

In fact, it may even be useful to have this side-effect of Python. Since you know what values evaluates to false, you can use this to trigger the default value without using None specifically (an error object, for example).

In some languages this behavior is referred to as the Elvis operator.

想你只要分分秒秒 2024-10-24 03:23:37

严格来说,

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")

但请注意,由于这是一个函数,因此如果第一个值不是 None,它不会像条件运算符那样短路;相反,即使第二个参数最终没有被使用,它也会评估两个参数。例如,如果 a 不是 None,则 notNone(a, get_some_value()) 仍会调用 get_some_value,但是 a if a is not None else get_some_value() 不会(运算符可以短路,条件运算符也可以)。

Strictly,

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

Otherwise, s = False will become "default value", which may not be what was intended.

If you want to make this shorter, try:

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

other = notNone(s, "default value")

Note, though, that since that's a function, it won't short-circuit like the conditional operator would if the first value isn't None; instead, it will evaluate both arguments even through the second one doesn't end up being used. For example, if a is not None, notNone(a, get_some_value()) will still call get_some_value, but a if a is not None else get_some_value() won't (operators can short-circuit, and the conditional operator does).

暗恋未遂 2024-10-24 03:23:37

下面的函数将返回第一个不是 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)

即使第一个参数不是 None,reduce() 也可能不必要地迭代所有参数,所以你也可以使用这个版本:

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

Here's a function that will return the first argument that isn't 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() might needlessly iterate over all the arguments even if the first argument is not None, so you can also use this version:

def coalesce(*arg):
  for el in arg:
    if el is not None:
      return el
  return None
迷离° 2024-10-24 03:23:37

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

model?.data()?.first()

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

考虑向语言中添加 null 感知运算符的相关 PEP 是 PEP 505 与该文档相关的讨论位于 python-ideas 线程。

In case you need to chain more than one null-conditional operation such as:

model?.data()?.first()

This is not a problem easily solved with or. It also cannot be solved with .get() which requires a dictionary type or similar (and cannot be nested anyway) or getattr() which will throw an exception when NoneType doesn't have the attribute.

The relevant PEP considering adding null-aware operators to the language is PEP 505 and the discussion relevant to the document is in the python-ideas thread.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文