字典文字中可以有可选键吗?

时间:2018-06-30 21:49:45

标签: python dictionary dictionary-comprehension

是否可以在dict文字中包含“可选”键,而不是将其添加到if语句中?

像这样:

a = True
b = False
c = True
d = False

obj = {
    "do_a": "args for a" if a,
    "do_b": "args for b" if b,
    "do_c": "args for c" if c,
    "do_d": "args for d" if d,
}

#expect:
obj == {
    "do_a": "args for a",
    "do_c": "args for c",
}

根据上下文编辑: 我知道该怎么做:)我只想避免使用if语句,因为我的对象是代表声明性逻辑的大数据块,因此移动内容有点像“意大利面条式编码”,是“它根本就意味着程序性。 我希望对象的值“看起来像是什么意思”作为查询。

这实际上是一个Elasticsearch查询,因此看起来像这样:

{
    "query": {
        "bool": {
            "must": [
                 <FILTER A>,
                 <FILTER B>,  # would like to make this filter optional
                 <FILTER C>,
                 {
                     "more nested stuff" : [ ... ]
                 }
             ],
             "other options": [ ... ]
        },
        "other options": [ ... ]
    },
    "other options": [ ... ]
}

我可能会怀疑的目标是使它看起来像一个查询,您可以查看它并了解它的形状,而不必通过if进行跟踪。 即,没有“过滤器”:[f表示过滤器中的f,如果启用f。 因为然后您必须去寻找过滤器,这些过滤器在这里都是可选的常量

4 个答案:

答案 0 :(得分:5)

首先定义键,参数和布尔变量的列表:

keys = ["do_a", "do_b", ...]
args = ["args for a", "args for b", ...]
mask = [a, b, ...]

现在,使用obj列表构造mask以确定要插入的键:

obj = {k : a for k, a, m in zip(keys, args, mask) if m}

或者,

obj = {}
for k, a, m in zip(keys, args, mask):
    if m:
        obj[k] = a

答案 1 :(得分:5)

否,您不能在文字中包含“可选值”。文字表达式的结果总是 插入结果中。

但是我认为无论如何遵循一个明确的if语句可能会更好:

a = True
b = False
c = True
d = False

obj = {}
if a: obj["do_a"] = "args for a"
if b: obj["do_b"] = "args for b"
if c: obj["do_c"] = "args for c"
if d: obj["do_d"] = "args for d"

在您真的不喜欢if的情况下,只需提及一些替代方法:

  • 如果参数为“ false” -ey,还可以使用其他值,然后过滤字典:

    _to_remove = object()
    
    obj = {
        "do_a": "args for a" if a else _to_remove,
        "do_b": "args for b" if b else _to_remove,
        "do_c": "args for c" if c else _to_remove,
        "do_d": "args for d" if d else _to_remove,
    }
    
    obj = {key: value for key, value in obj.items() if value is not _to_remove}
    
  • 或使用itertools.compress和内置的dict

    key_value_pairs = [
        ("do_a", "args for a"), 
        ("do_b", "args for b"), 
        ("do_c", "args for c"), 
        ("do_d", "args for d")
    ]
    
    from itertools import compress
    
    obj = dict(compress(key_value_pairs, [a, b, c, d]))
    

答案 2 :(得分:1)

这是另一种方法。给定与您相同的定义:

a = True
b = False
c = True
d = False

然后,您可以将文字构造为三个成员元组:

li=[
    ("do_a", "args for a",a),
    ("do_b", "args for b",b),
    ("do_c", "args for c",c),
    ("do_d", "args for d",d)
]

这等效于将zip与三个文字列表一起使用,但人眼可能会更容易理解较短的列表。

然后有条件地构造您的字典:

>>> dict([(k,v) for k,v,f in li if f])
{'do_c': 'args for c', 'do_a': 'args for a'}

在帖子中已作了澄清,您可以将函数用作dict值,并在创建dict(或更新dict)时简单地调用该函数:

def filter_a():
    # some expensive function...
    return "<FILTER A>"

def filter_b():
    return "<FILTER B>"  

def filter_c():
    return "<FILTER C>"    

def filter_d():
    return "<FILTER D>" 

li=[
    ("do_a", filter_a, a),
    ("do_b", filter_b, b),
    ("do_c", filter_c, c),
    ("do_d", filter_d, d)
]

然后仅将相关的过滤器函数按构造方式调用:

>>> dict((k,v()) for k,v,f in li if f)
{'do_c': '<FILTER C>', 'do_a': '<FILTER A>'}

然后永远不会调用B和D。

更好地编写逻辑,以使FILTER X是一种生成器,并仅在需要时返回数据。

答案 3 :(得分:1)

我认为答案是“否”,正如其他答案所述,但这是我到目前为止得到的最接近的答案...

虽然它略微位于'wtf'的'令人讨厌的'一侧

a = True
b = False
c = True
d = False

obj = {
    **({"do_a": "args for a"} if a else {}),
    **({"do_b": "args for b"} if b else {}),
    **({"do_c": "args for c"} if c else {}),
    **({"do_d": "args for d"} if d else {}),
}

#expect:
assert(obj == {
        "do_a": "args for a",
        "do_c": "args for c",
    })

或者如果您想在某些函数中添加可选性:

def maybe(dictionary, condition, default=None):
    return dictionary if condition else default or {}

obj = {
    **maybe({"do_a": "args for a"}, a),
    **maybe({"do_b": "args for b"}, b),
    **maybe({"do_c": "args for c"}, c),
    **maybe({"do_d": "args for d"}, d),
}

这种代码的问题是条件离结果越来越远(假设我们最终将大的dict传递给maybe的第一个参数)。