web-dev-qa-db-fra.com

Existe-t-il un équivalent Python de l’opérateur C # nul)?

En C #, il existe un opérateur null-coalescing (écrit sous la forme ??) qui permet une vérification de zéro simple (courte) lors de l'affectation:

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

Existe-t-il un équivalent python?

Je sais que je peux faire:

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

Mais y a-t-il un moyen encore plus court (où je n'ai pas besoin de répéter s)?

242
Klaus Byskov Pedersen
other = s or "some default value"

Ok, il faut clarifier le fonctionnement de l’opérateur or. C'est un opérateur booléen, donc cela fonctionne dans un contexte booléen. Si les valeurs ne sont pas booléennes, elles sont converties en booléens pour les besoins de l'opérateur.

Notez que l'opérateur or ne renvoie pas uniquement True ou False. Au lieu de cela, il renvoie le premier opérande si le premier opérande est évalué à true et le deuxième opérande si le premier opérande est évalué à false.

Dans ce cas, l'expression x or y Renvoie x s'il s'agit de True ou est évaluée à true lors de la conversion en booléen. Sinon, il retourne y. Dans la plupart des cas, cela sert exactement au même objectif que l'opérateur à coalescence nulle de C♯, mais gardez à l'esprit que:

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

Si vous utilisez votre variable s pour contenir quelque chose qui soit soit une référence à l'instance d'une classe, soit None (tant que votre classe ne définit pas les membres __nonzero__() et __len__()), il est sécurisé d'utiliser la même sémantique que l'opérateur null-coalescing.

En fait, il pourrait même être utile d’avoir cet effet secondaire de Python. Puisque vous savez quelles valeurs sont évaluées sur false, vous pouvez l'utiliser pour déclencher la valeur par défaut sans utiliser spécifiquement None (un objet d'erreur, par exemple).

Dans certaines langues, ce comportement est appelé opérateur Elvis .

350
Juliano

Strictement,

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

Autrement, s = False va devenir "default value", qui peut ne pas être ce qui était prévu.

Si vous voulez raccourcir cela, essayez:

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

other = notNone(s, "default value")
51
Hugh Bothwell

Voici une fonction qui renverra le premier argument qui n'est pas 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() pourrait itérer inutilement sur tous les arguments même si le premier argument n'est pas None, vous pouvez donc également utiliser cette version:

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

En plus de la réponse de Juliano à propos du comportement de "ou": c'est "rapide"

>>> 1 or 5/0
1

Alors parfois, cela pourrait être un raccourci utile pour des choses comme

object = getCachedVersion() or getFromDB()
3
Orca

Je me rends compte que la réponse est donnée, mais il existe une autre option lorsque vous traitez avec des objets.

Si vous avez un objet qui pourrait être:

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

Vous pouvez utiliser:

obj.get(property_name, value_if_null)

Comme:

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

En ajoutant {} comme valeur par défaut, si "name" est manquant, un objet vide est renvoyé et transmis au prochain get. Ceci est similaire à null-safe-navigation en C #, ce qui ressemblerait à obj?.name?.first.

3
Craig

En ce qui concerne les réponses de Hugh Bothwell, @mortehu et @glglgl.

Configuration du jeu de données pour les tests

import random

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

Définir les implémentations

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)

Faire fonction de test

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

Résultats sur mac i7 à 2,7 Ghz avec 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

Clairement le not_none _ function répond correctement à la question du PO et gère le problème de "fausseté". C'est aussi le plus rapide et le plus facile à lire. Si vous appliquez la logique dans de nombreux endroits, c'est clairement la meilleure voie à suivre.

Si vous avez un problème pour lequel vous souhaitez trouver la 1ère valeur non nulle dans une valeur itérable, la réponse de @ mortehu est la solution. Mais c’est une solution à un problème différent que OP, bien qu’il puisse gérer partiellement ce cas. Il ne peut pas prendre une valeur itérable ET une valeur par défaut. Le dernier argument serait la valeur par défaut renvoyée, mais dans ce cas, vous ne transmettriez pas une variable itérable, car il n'est pas explicite que le dernier argument soit une valeur par défaut.

Vous pourriez alors faire ci-dessous, mais j'utiliserais quand même not_null pour le cas d'utilisation à valeur unique.

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