仅在满足条件的情况下添加到dict


78

我正在使用urllib.urlencode构建Web POST参数,但是,如果None它们不存在其他值,我只想添加一些值。

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

效果很好,但是如果我将orange变量设置为可选,如何防止将其添加到参数中?这样的东西(伪代码):

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

我希望这已经足够清楚了,有人知道如何解决吗?


如果存在可接受的默认值,则可以使用'orange': orange if orange else default语法。
2013年

Answers:


71

创建初始字母后,您必须单独添加密钥dict

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

Python没有语法将键定义为条件键;如果您已经按顺序拥有所有内容,则可以使用dict理解:

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

但这不是很可读。

另一个选择是使用字典拆包,但是对于单个键来说,可读性不是那么高:

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

我个人永远不会使用它,因为它太hacky了,而且不如使用单独的语句那么明确和清晰if。正如Python禅宗所言:可读性至关重要。


1
对于Python 3.5及更高版本:自从实施PEP-0448(拟于2013年6月29日开始)以来,stackoverflow.com /a/55341342/563970应该是答案
Bart

1
@Bart:这在风格上是非常多的选择。仅使用一把钥匙,使用起来**({key: value} if test else {})确实不易读。
马丁·彼得斯

1
当然,这是一种风格选择,对于单个选择来说,这可能是过大的选择。我一直在使用它向{key: value}嵌套dict添加对,其中键和值都是从其他键和值派生(组成)的。在我的情况下,这样做if something: ...肯定会降低可读性(由于嵌套,然后必须应用两次或更多次)。YMMV在这里视情况而定。
巴特

快速说明:在今天的情况下,我的条件dict键正好位于嵌套dict和list文字的大型结构的中间(一个mongoDB聚合管道)。将条件放在结构中的dict位置确实有帮助(尽管明天我可能会决定它看起来太像注入漏洞!)。
戴夫·格雷戈里

36

为了搭载sqreept的答案,以下是该类的子类,dict其行为符合预期:

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

这将允许将现有键的值更改None,但是分配None给不存在的键是无操作的。如果你想设置的项目None,以除去从如果它已经存在的字典,你可以这样做:

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

如果在构造过程中传递它们,则None 可以获取的值。如果要避免这种情况,请添加一种__init__方法将其过滤掉:

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

您也可以通过编写使其通用,以便在创建字典时传递所需的条件:

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

谢谢。我能够将其与此答案结合起来使用stackoverflow.com/a/2588648/2860243
Leonardo Ruiz

1
新的更新方法可能不会单独完成。CPython在执行dict-to-dict更新时会绕过特殊方法(它根据对象的内存结构确定)。您可能需要避免直接将dict子类化(您可以将__class__dict设置为通过isinstance检查)。这可能不适用于这种情况(我正在做相反的事情,提取
时而

这适用于添加新值。如果您希望构造函数也起作用,则也需要覆盖init并为None过滤掉kwargs值。
奥利


17

相当老的问题,但这是使用以下事实的替代方法:用空的dict更新一个dict不会执行任何操作。

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)

哦,非常整洁。我最喜欢这个答案!
RCross '16

同意,除了您需要通过循环更新多次来完成的所有额外工作:摆脱for循环并执行以下操作:params.update({key: val for key, val in kwargs if val is not None})
DylanYoung

6

我做到了 希望对您有所帮助。

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

预期产量: {'orange': 10, 'apple': 23}

虽然,如果orange = None,那么将只有一个条目None:None。例如考虑一下:

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

预期产量: {None: None, 'apple': 23}


1
这是一个巧妙的把戏。这样一来,最后只需要清除一个键:None。我建议仅对键执行条件(如果您担心其中的值,只需将其添加None: None为dict声明的最后一行),然后再执行即可del a[None]
DylanYoung '19

1
这是最好的答案。只需添加即可a.pop(None),完美无缺
raullalves

这是一个坏习惯。如果该语言不支持,最好不要通过此操作添加额外的操作(例如a.pop,del a [None]等)。
FernandoMartínez20年

3

您可以在分配后清除无:

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
同样,d = {k:v for k,v in d.items() if v}
Ryan Artecona 2013年

8
这还将清除评估为False的值。您应该if dictparams[k] is None改为这样做。
PhilipGarnero 2015年

d = {k:v for k,v in d.items() if v is not None},然后
CharlesB

3

另一个有效的答案是,您可以创建自己的类似dict的容器,该容器不存储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

产量:

{'orange': 'orange'}

漂亮优雅,我只加了一个默认的接受值DEF得到(自我,键,DEFAULT_VALUE =无):返回self.container.get(键,DEFAULT_VALUE)
瓦尔费利佩·菲卡Urzúa

1

我真的很喜欢这里答案中的巧妙技巧:https//stackoverflow.com/a/50311983/3124256

但是,它有一些陷阱:

  1. 重复if测试(重复键和值)
  2. 讨厌None: None的结果dict

为避免这种情况,可以执行以下操作:

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]

预期产量: {'apple': 23}

注意:该None: None条目确保两件事:

  1. None键将始终存在(del会不会引发错误)
  2. “无值”的内容在字典中将永远不存在(以防del以后忘记)

如果您不担心这些事情,则可以将其省略,然后将del包裹在其中try...except(或Nonedel输入之前检查键是否存在)。要另外选择地址2,您还可以对值(除了键)进行条件检查。


0
fruits = [("apple", get_apple()), ("orange", get_orange()), ...]

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

因此,我们需要getter为每个变量设置一个。为什么不这样做:fruits={"apple", "orange"}; d=vars(); params = urllib.urlencode({ fruit: val for fruit, val in d.items() if fruit in fruits and val is not None })
DylanYoung

0

有一个违反直觉但可靠的技巧,可以重用您要排除的其他道具名称。

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

在这种情况下,后一个“苹果”将覆盖前一个“苹果”,从而有效地将其删除。请注意,条件表达式应高于真实表达式。


1
我不建议这样做,因为这取决于您编写密钥的顺序。容易出现错误。
Nikhil Wagh '04

0

您可以使用字典理解来使用单个条件处理所有可选项目:

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

dictparams结果将不包含"orange"在这种情况下,由于orangeNone

{'apple': 'green'}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.