Existe um equivalente em Python do operador de coalescência nula em C #?

301

Em C #, há um operador de coalescência nula (escrito como ??) que permite uma verificação nula fácil (curta) durante a atribuição:

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

Existe um equivalente python?

Eu sei que posso fazer:

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

Mas existe uma maneira ainda mais curta (onde eu não preciso repetir s)?

Klaus Byskov Pedersen
fonte
14
O ??operador é proposto como PEP 505 .
200_success 20/10/16

Respostas:

421
other = s or "some default value"

Ok, deve ser esclarecido como o oroperador funciona. É um operador booleano, portanto, funciona em um contexto booleano. Se os valores não forem booleanos, eles serão convertidos em booleano para os propósitos do operador.

Observe que o oroperador não retorna apenas Trueou False. Em vez disso, ele retornará o primeiro operando se o primeiro operando for avaliado como verdadeiro e retorna o segundo operando se o primeiro operando for avaliado como falso.

Nesse caso, a expressão x or yretornará xse for Trueou for avaliada como true quando convertida em booleana. Caso contrário, ele retornará y. Na maioria dos casos, isso servirá para o mesmo objetivo do operador coalescente nulo de C♯, mas lembre-se:

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

Se você usar sua variável spara armazenar algo que seja uma referência à instância de uma classe ou None(desde que sua classe não defina membros __nonzero__()e__len__() ), é seguro usar a mesma semântica que o operador de coalescência nula.

De fato, pode até ser útil ter esse efeito colateral do Python. Como você sabe quais valores são avaliados como falsos, é possível usá-lo para acionar o valor padrão sem usar Noneespecificamente (um objeto de erro, por exemplo).

Em alguns idiomas, esse comportamento é chamado de operador Elvis .

Juliano
fonte
3
Isso funcionará da mesma maneira? Quero dizer, isso vai quebrar se sfor um valor válido, mas não for verdade? (Eu não sei Python, então eu não tenho certeza se o conceito de 'truthy' se aplica.)
Chao
9
O número 0, Nonee os contêineres vazios (incluindo cadeias) são considerados falsos, além da constante False. Quase todo o resto é considerado verdadeiro. Eu diria que o principal perigo aqui seria que você obteria um valor verdadeiro, mas sem string, mas isso não será um problema em alguns programas.
Kindall
25
O uso desse outro obterá o valor padrão se s for Nenhum ou Falso , que pode não ser o desejado.
pafcu
6
Existem muitos erros obscuros causados ​​por isso também. Por exemplo, antes do Python 3.5, datetime.time(0)também era falso!
Antti Haapala
19
Isto é mau. Eu recomendo adicionar um aviso sobre suas armadilhas. E recomendando não usá-lo.
Mateen Ulhaq
61

Estritamente,

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

Caso contrário, s = Falsese tornará "default value", o que pode não ser o que foi planejado.

Se você quiser tornar isso mais curto, tente:

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

other = notNone(s, "default value")
Hugh Bothwell
fonte
1
Consider x()?.y()?.z()
nurettin 24/04
40

Aqui está uma função que retornará o primeiro argumento que não é 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()pode desnecessariamente repetir todos os argumentos, mesmo que o primeiro argumento não seja None, então você também pode usar esta versão:

def coalesce(*arg):
  for el in arg:
    if el is not None:
      return el
  return None
mortehu
fonte
23
def coalesce(*arg): return next((a for a in arg if a is not None), None)faz o mesmo que o seu último exemplo em uma linha.
glglgl
2
Eu entendo que as pessoas querem explicar se sytnax, etc., mas coalescer leva uma lista de argumentos arbitrária, então essa deve ser realmente a resposta principal.
Eric Twilegar
2
glglgl tem a melhor resposta. Usei timeit em um grande conjunto de testes e a implementação de redução é inaceitavelmente lenta, a versão multilinha para / se a versão é mais rápida e a próxima implementação está um pouco atrasada. A próxima versão é a melhor em geral quando se considera simplicidade e concisão.
Clay #
3
@glglgl tem um trecho interessante. Infelizmente, como o Python não tem senha, o agrupamento como esse não é um curto-circuito; todos os argumentos são avaliados antes da execução do código.
user1338062
Consider x()?.y()?.z()
nurettin 24/04
5

Sei que isso é respondido, mas há outra opção quando você está lidando com objetos.

Se você tiver um objeto que possa ser:

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

Você pode usar:

obj.get(property_name, value_if_null)

Gostar:

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

Ao adicionar {}como valor padrão, se "name" estiver ausente, um objeto vazio será retornado e passado para a próxima obtenção. Isso é semelhante à navegação segura com segurança nula em C #, como seria obj?.name?.first.

Craig
fonte
Nem todos os objetos têm .get, isso só funciona para dict-como objetos
timdiels
Estou enviando uma edição de resposta para cobrir getattr()também.
dgw 20/08/19
geton dict não usa o parâmetro padrão se o valor for Nenhum, mas usa o parâmetro padrão se o valor não existir porque a chave não está no dict. {'a': None}.get('a', 'I do not want None')ainda lhe dará Nonecomo resultado.
Patrick Mevzek 29/01
3

Além da resposta de Juliano sobre o comportamento de "ou": é "rápido"

>>> 1 or 5/0
1

Às vezes, pode ser um atalho útil para coisas como

object = getCachedVersion() or getFromDB()
Orca
fonte
17
O termo que você procura é "curto-circuito".
Jpmc26 7/11
1

Adicionalmente à resposta do @Bothwells (que eu prefiro) para valores únicos, a fim de verificar de forma nula os valores de retorno da função, você pode usar o novo walrus-operator (desde python3.8):

def test():
    return

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

Portanto, a testfunção não precisa ser avaliada duas vezes (como em a = 2 if test() is None else test())

Henhuy
fonte
1

Caso você precise aninhar mais de uma operação coalescente nula, como:

model?.data()?.first()

Este não é um problema facilmente resolvido or. Também não pode ser resolvido com o .get()que requer um tipo de dicionário ou similar (e não pode ser aninhado de qualquer maneira) ougetattr() que gerará uma exceção quando NoneType não tiver o atributo

O pip relevante que considera a adição de coalescência nula ao idioma é PEP 505 e a discussão relevante para o documento está no segmento python-ideas .

nurettin
fonte
0

Em relação às respostas de @Hugh Bothwell, @mortehu e @glglgl.

Conjunto de dados de configuração para teste

import random

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

Definir implementações

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)

Faça a função de teste

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

Resultados no mac i7 @ 2.7Ghz usando 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

Claramente o not_none função responde à pergunta do OP corretamente e lida com o problema de "falsidade". É também o mais rápido e fácil de ler. Se aplicar a lógica em muitos lugares, é claramente o melhor caminho a percorrer.

Se você tiver um problema em que deseja encontrar o 1º valor não nulo em uma iterável, a resposta do @ mortehu é o caminho a percorrer. Mas é uma solução para um problema diferente do OP, embora possa lidar parcialmente com esse caso. Não pode levar um AND iterável e um valor padrão. O último argumento seria o valor padrão retornado, mas você não passaria um iterável nesse caso, assim como não está explícito que o último argumento seja o valor padrão.

Você poderia fazer isso abaixo, mas eu ainda usaria not_nullpara o caso de uso de valor único.

def coalesce(*args, **kwargs):
    default = kwargs.get('default')
    return next((a for a in arg if a is not None), default)
debo
fonte
0

Para aqueles como eu, que tropeçamos aqui, procurando uma solução viável para esse problema, quando a variável pode ser indefinida, o mais próximo que eu chego é:

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

Observe que uma string é necessária ao fazer check-in em globais, mas depois a variável real é usada ao verificar o valor.

Mais sobre a existência de variáveis: Como verifico se existe uma variável?

Itaca
fonte
1
(variablename or False) == Trueé o mesmo quevariablename == True
mic
-2
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

se você não conseguir encontrar o nome no dicionário, ele retornará o valor padrão; se o nome existir, ele adicionará qualquer valor existente com 1.

espero que isso possa ajudar

Angelo
fonte
1
Olá, bem-vindo ao Stack Overflow. Quais são as novas informações adicionadas pela sua resposta que ainda não foram cobertas pelas respostas existentes? Veja a resposta de @ Craig por exemplo
Gricey 18/10/19
-6

As duas funções abaixo são muito úteis para lidar com muitos casos de teste variáveis.

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
Mike Stabile
fonte
5
Esse tipo de coisa adiciona várias terminologias ligeiramente diferentes (por exemplo, "null" e "nz", que não significam nada no contexto do Python), importadas de outras linguagens, além de variantes (estrita ou não estrita!). Isso apenas adiciona confusão. Verificações explícitas "é nenhuma" são o que você deve usar. Além disso, você não obtém o benefício de nenhuma semântica de atalho que os operadores podem fazer quando você usa uma chamada de função.
Spookylukey