web-dev-qa-db-fra.com

Ajouter à un dict uniquement si une condition est remplie

J'utilise urllib.urlencode pour construire des paramètres web POST, cependant il y a quelques valeurs que je veux seulement ajouter si une valeur autre que None existe pour eux.

Apple = 'green'
orange = 'orange'
params = urllib.urlencode({
    'Apple': Apple,
    'orange': orange
})

Cela fonctionne très bien, mais si je rends la variable orange facultative, comment puis-je l'empêcher d'être ajoutée aux paramètres? Quelque chose comme ça (pseudocode):

Apple = 'green'
orange = None
params = urllib.urlencode({
    'Apple': Apple,
    if orange: 'orange': orange
})

J'espère que c'était assez clair, quelqu'un sait-il comment résoudre ce problème?

52
user1814016

Vous devrez ajouter la clé séparément, après avoir créé le dict initial:

params = {'Apple': Apple}
if orange is not None:
    params['orange'] = orange
params = urllib.urlencode(params)

Python n'a pas de syntaxe pour définir une clé comme conditionnelle; vous pouvez utiliser une compréhension dict si vous aviez déjà tout dans une séquence:

params = urllib.urlencode({k: v for k, v in (('orange', orange), ('Apple', Apple)) if v is not None})

mais ce n'est pas très lisible.

45
Martijn Pieters

Pour utiliser la réponse de sqreept, voici une sous-classe de dict qui se comporte comme vous le souhaitez:

class DictNoNone(dict):
    def __setitem__(self, key, value):
        if key in self or value is not None:
            dict.__setitem__(self, key, value)


d = DictNoNone()
d["foo"] = None
assert "foo" not in d

Cela permettra aux valeurs des clés existantes d'être modifiées à None, mais assigner None à une clé qui n'existe pas est un no-op. Si vous vouliez définir un élément sur None sur supprimer dans le dictionnaire s'il existe déjà, vous pouvez le faire:

def __setitem__(self, key, value):
    if value is None:
        if key in self:
            del self[key]
    else:
        dict.__setitem__(self, key, value)

Les valeurs de Nonecan entrent si vous les passez pendant la construction. Si vous voulez éviter cela, ajoutez un __init__ méthode pour les filtrer:

def __init__(self, iterable=(), **kwargs):
    for k, v in iterable:
        if v is not None: self[k] = v
    for k, v in kwargs.iteritems():
        if v is not None: self[k] = v

Vous pouvez également le rendre générique en l'écrivant afin de pouvoir passer la condition souhaitée lors de la création du dictionnaire:

class DictConditional(dict):
    def __init__(self, cond=lambda x: x is not None):
        self.cond = cond
    def __setitem__(self, key, value):
        if key in self or self.cond(value):
            dict.__setitem__(self, key, value)

d = DictConditional(lambda x: x != 0)
d["foo"] = 0   # should not create key
assert "foo" not in d
28
kindall

Assez vieille question mais voici une alternative utilisant le fait que la mise à jour d'un dict avec un dict vide ne fait rien.

def urlencode_func(Apple, orange=None):
    kwargs = locals().items()
    params = dict()
    for key, value in kwargs:
        params.update({} if value is None else {key: value})
    return urllib.urlencode(params)
13
lindsay.stevens.au
params = urllib.urlencode({
    'Apple': Apple,
    **({'orange': orange} if orange else {}),
})

Vous pouvez effacer Aucun après l'affectation:

Apple = 'green'
orange = None
dictparams = {
    'Apple': Apple,
    'orange': orange
}
for k in dictparams.keys():
    if not dictparams[k]:
        del dictparams[k]
params = urllib.urlencode(dictparams)
3
sqreept

J'ai fait ça. J'espère que cette aide.

Apple = 23
orange = 10
a = {
    'Apple' : Apple,
    'orange' if orange else None : orange if orange else None
}

Production attendue : {'orange': 10, 'Apple': 23}

Bien que, si orange = None, il y aura alors une seule entrée pour None:None. Par exemple, considérez ceci:

Apple = 23
orange = None
a = {
    'Apple' : Apple,
    'orange' if orange else None : orange if orange else None
}

Production attendue : {None: None, 'Apple': 23}

2
Nikhil Wagh

Une autre réponse valable est que vous pouvez créer votre propre conteneur de type dict qui ne stocke pas de valeurs None.

class MyDict:
    def __init__(self):
        self.container = {}
    def __getitem__(self, key):
        return self.container[key]
    def __setitem__(self, key, value):
        if value != None:
            self.container[key] = value
    def __repr__(self):
        return self.container.__repr__()

a = MyDict()
a['orange'] = 'orange';
a['lemon'] = None

print a

rendements:

{'orange': 'orange'}
2
sqreept

J'aime vraiment l'astuce soignée dans la réponse ici: https://stackoverflow.com/a/50311983/3124256

Mais, il y a quelques pièges:

  1. Dupliquer les tests if (répétés pour la clé et la valeur)
  2. Pesky None: None entrée dans le dict résultant

Pour éviter cela, vous pouvez procéder comme suit:

Apple = 23
orange = None
banana = None
a = {
    'Apple' if Apple else None: Apple,
    'orange' if orange else None : orange,
    'banana' if banana else None: banana,
    None: None,
}
del a[None]

Production attendue : {'Apple': 23}

Noter la None: None l'entrée garantit deux choses:

  1. La touche None sera toujours présente (del ne générera pas d'erreur)
  2. Le contenu de 'Aucune valeur' ​​n'existera jamais dans le dict (au cas où vous oublieriez de del après)

Si vous n'êtes pas inquiet à propos de ces choses, vous pouvez le laisser de côté et envelopper le del dans un try...except (ou vérifiez si la touche None est présente avant deling). Pour adresser le numéro 2 alternativement, vous pouvez également mettre la vérification conditionnelle sur la valeur (en plus de la clé).

0
DylanYoung
fruits = [("Apple", get_Apple()), ("orange", get_orange()), ...]

params = urllib.urlencode({ fruit: val for fruit, val in fruits if val is not None })
0
XORcist