Есть ли в Python эквивалент нуль-сливающегося оператора C #? - PullRequest
235 голосов
/ 12 февраля 2011

В C # есть оператор слияния нуля (записанный как ??), который позволяет легко (коротко) проверять ноль во время присваивания:

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

Есть ли эквивалент Python?

Я знаю, что могу сделать:

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

Но есть ли еще более короткий путь (где мне не нужно повторять s)?

Ответы [ 7 ]

344 голосов
/ 12 февраля 2011
other = s or "some default value"

Хорошо, необходимо уточнить, как работает оператор or. Это логический оператор, поэтому он работает в логическом контексте. Если значения не являются логическими, они преобразуются в логические для целей оператора.

Обратите внимание, что оператор or не возвращает только True или False. Вместо этого он возвращает первый операнд, если первый операнд оценивается как true, и возвращает второй операнд, если первый операнд оценивается как false.

В этом случае выражение x or y возвращает x, если оно равно True, или возвращает значение true при преобразовании в логическое значение. В противном случае возвращается y. В большинстве случаев это будет служить той же цели, что и оператор объединения нуля в C nu, но имейте в виду:

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. Поскольку вы знаете, какие значения оцениваются как ложные, вы можете использовать это для запуска значения по умолчанию, не используя None специально (например, объект ошибки).

В некоторых языках это поведение называется оператором Элвиса .

51 голосов
/ 12 февраля 2011

строго

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")
37 голосов
/ 27 апреля 2013

Вот функция, которая будет возвращать первый аргумент, который не является 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)

redu () может без необходимости перебирать все аргументы, даже если первый аргумент не None, так что вы также можетеиспользуйте эту версию:

def coalesce(*arg):
  for el in arg:
    if el is not None:
      return el
  return None
3 голосов
/ 24 июня 2014

В дополнение к ответу Джулиано о поведении «или»: это "быстро"

>>> 1 or 5/0
1

Так что иногда это может быть полезным ярлыком для таких вещей, как

object = getCachedVersion() or getFromDB()
2 голосов
/ 13 июля 2018

Я понимаю, что это ответ, но есть другой вариант, когда вы имеете дело с объектами.

Если у вас есть объект, который может быть:

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

Вы можете использовать:

obj.get(property_name, value_if_null)

Как:

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

При добавлении {} в качестве значения по умолчанию, если «имя» отсутствует, пустой объект возвращается и передается для следующего получения. Это похоже на null-safe-navigation в C #, которое будет выглядеть как obj?.name?.first.

0 голосов
/ 07 июня 2019

Относительно ответов @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)

Результаты для Mac i7 @ 2,7 ГГц с использованием 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

Очевидно, что функция not_none правильно отвечает на вопрос ОП и решает проблему «ложных». Это также самый быстрый и легкий для чтения. Если применять логику во многих местах, это, безусловно, лучший путь.

Если у вас есть проблема, когда вы хотите найти 1-е ненулевое значение в итерируемом, то ответ @ 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)
0 голосов
/ 14 марта 2016

Две нижеприведенные функции оказались очень полезными при работе со многими переменными вариантами тестирования.

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