Only add to a dict if a condition is met

后端 未结 11 2050
鱼传尺愫
鱼传尺愫 2020-12-05 03:57

I am using urllib.urlencode to build web POST parameters, however there are a few values I only want to be added if a value other than None exists

相关标签:
11条回答
  • 2020-12-05 04:20
    fruits = [("apple", get_apple()), ("orange", get_orange()), ...]
    
    params = urllib.urlencode({ fruit: val for fruit, val in fruits if val is not None })
    
    0 讨论(0)
  • 2020-12-05 04:23

    There is a counter-intuitive but reliable hack, to reuse the other prop name you want to exclude it.

    {
        'orange' if orange else 'apple': orange,
        'apple': apple,
    }
    

    In this case, the latter 'apple' will override the previous 'apple' effectively removing it. Note that the conditional expressions should go above the real ones.

    0 讨论(0)
  • 2020-12-05 04:25

    You'll have to add the key separately, after the creating the initial dict:

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

    Python has no syntax to define a key as conditional; you could use a dict comprehension if you already had everything in a sequence:

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

    but that's not very readable.

    Another option is to use dictionary unpacking, but for a single key that's not all that more readable:

    params = urllib.urlencode({
        'apple': apple,
        **({'orange': orange} if orange is not None else {})
    })
    

    I personally would never use this, it's too hacky and is not nearly as explicit and clear as using a separate if statement. As the Zen of Python states: Readability counts.

    0 讨论(0)
  • 2020-12-05 04:26

    To piggyback on sqreept's answer, here's a subclass of dict that behaves as desired:

    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
    

    This will allow values of existing keys to be changed to None, but assigning None to a key that does not exist is a no-op. If you wanted setting an item to None to remove it from the dictionary if it already exists, you could do this:

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

    Values of None can get in if you pass them in during construction. If you want to avoid that, add an __init__ method to filter them out:

    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
    

    You could also make it generic by writing it so you can pass in the desired condition when creating the dictionary:

    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
    
    0 讨论(0)
  • 2020-12-05 04:34

    You can deal with all optional items using a single condition by using a dictionary comprehension:

    apple = "red"
    orange = None
    dictparams = {
        key: value for key, value in
        {
            "apple": apple,
            "orange": orange
        }.items()
        if value is not None
    }
    

    The dictparams result will not contain "orange" in this case, because orange is None:

    {'apple': 'green'}
    
    0 讨论(0)
提交回复
热议问题