Существует ли эквивалент Python оператора С# null-coalescing?

В С# существует оператор null-coalescing (написанный как ??), который позволяет легко (коротко) проверять нуль во время назначения:

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

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

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

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

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

Ответ 1

other = s or "some default value"

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

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

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

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 (например, объект ошибки).

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

Ответ 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")

Ответ 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

Ответ 4

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

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

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

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

obj.get(property_name, value_if_null)

Подобно:

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

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

Ответ 5

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

>>> 1 or 5/0
1

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

object = getCachedVersion() or getFromDB()

Ответ 6

Относительно ответов @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 правильно отвечает на вопрос OP и решает проблему "ложных". Это также самый быстрый и легкий для чтения. Если применять логику во многих местах, это, безусловно, лучший путь.

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

Ответ 7

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.

надеюсь, это поможет

Ответ 8

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

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