避免使用 if 语句检查字典中的键

Avoid using if statements for checking keys in dict

我正在尝试将一些值从数据映射到模板。
我只想在模板中已经存在的值(通过一些操作)中填充它们。
我的模板有数百个键,我的目标是在每次操作和赋值之前避免 if 语句。

if 语句的要点是推迟对我正在执行的操作的评估,因为它们执行起来可能很昂贵。任何解决方案都应考虑到这一点。

data = {
    'a':1,
    'b':2,
    'c':3,
    'd':4,
    'e':5
}

template1 = {
    'p':'Nan',
    'q':'Nan',
    'r':'Nan'
}

template2 = {
    'p':'Nan',
    's':'Nan',
    't':'Nan'
}

def func(template,data):
    if 'p' in template.keys():
        template['p'] = data['a']
    if 'q' in template.keys():
        template['q'] = data['b'][:2] + 'some manipulation'
    if 'r' in template.keys():
        template['r'] = data['c']
    if 's' in template.keys():
        template['s'] = data['d'] + 'some mainpulation'
    if 't' in template.keys():
        template['t'] = data['e']

我知道我遗漏了一些基本的东西,我的实际代码和要求非常复杂,我试图简化它们并将它们简化为这个简单的结构。 提前感谢您的帮助!

我唯一能想到的就是通过 for 循环使用某种字典和 运行 您的模板。如:

template_dict = {'p': 'a', 'q': 'b', 'r': 'c', 's': 'd', 't': 'e'}

def func(template, data):
    for key, value in template_dict.items():
        if key in template.keys():
            template[key] = data[value]

否则,我不确定您如何才能避免所有这些条件。

动态函数方法可能会让您摆脱所有的 if 和 else,但可能会使整个程序结构复杂化。

data = {
    'a':1,
    'b':2,
    'c':3,
    'd':4,
    'e':5
}

template1 = {
    'p': 'Nan',
    'q': 'Nan',
    'r': 'Nan'
}

template2 = {
    'p': 'Nan',
    's': 'Nan',
    't': 'Nan'
}

# first, define your complex logic in functions, accounting for every possible template key


def p_logic(data, x):
    return data[x]


def q_logic(data, x):
    return data[x][:2] + 'some manipulation'

# Then build a dict of every possible template key, the associated value and reference to one of the
# functions defined above


logic = {
    'p': {
        'value': 'a',
        'logic': p_logic
    },
    'q': {
        'value': 'b',
        'logic': q_logic
    },
}


def func(template, data):
    # for every key in a template, lookup that key in our logic dict
    # grab the value from the data
    # and apply the complex logic that has been defined for this template value 
    for item in template:  # template.keys() is not necessary!
        template[item] = logic[item]['logic'](data, logic[item]['value'])

您还可以使用 lambda 函数将操作直接存储在数据字典中,然后在使用此字典更新模板时检查从数据字典中检索到的任何值是否为 callable()。假设您不能修改数据字典中的键,那么这种方法仍然可以与 Jlove 建议的 template_dict 映射方法一起使用。

data = {
    'p': 1,
    'q': 2,
    'r': 3,
    's': 4,
    't': 5,
    'u': lambda x: x * 2
}

template1 = {
    'p':'Nan',
    'q':'Nan',
    'r':'Nan',
    'u': 2
}


def func(template, data):
    for key in template:
        if callable(data[key]):
           template[key] = data[key](template[key])
        else:
            template[key] = data[key]

#driver
func(template1, data)

for k in template1.items():
    print(k)

--- 基于评论的扩展解决方案 ---

与上面基本相同,只是展示了如何使用mapping dict来指导data dict和actions dict如何结合起来修改template dict。还展示了如何使用字典将键映射到函数。

from collections import defaultdict

def qManipulation(x):
    return x * 10

def sManipulation(x):
    return x * 3

data = {
'a':1,
'b':2,
'c':3,
'd':4,
'e':5
}

actions = {
'q': qManipulation,
's': sManipulation,
'u': lambda x: x * 7
}

tempToDataMap = defaultdict(lambda: None, {
'p': 'a',
'q': 'b',
'r': 'c',
's': 'd',
't': 'e'
})

template1 = {
'p':'Nan',
'q':'Nan',
'r':'Nan',
'u': 2
}

def func(template, data):
    for key, val in template.items():
        dataKey = tempToDataMap[key]
    
        # check if the template key corrosponds to a data dict key
        if dataKey is not None:

            # if key mapping from template to data is actually in data dict, use data value in template
            if dataKey in data:
                template[key] = data[dataKey]

            # if the template key is registered to an action in action dict, run action
            if key in actions:
                template[key] = actions[key](data[dataKey])
        

        # use this if you have a manipulation on a template field that is not populated by data.
        # this isn't present in the example, but could be handy if the template ever has default values other that Nan
        elif key in actions:
            template[key] = actions[key](template[key])


func(template1, data)

for k in template1.items():
    print(k)

这可能不是一个好主意,但您可以子类化 dict 并覆盖 __setitem__

class GuardDict(dict):
    def __setitem__(self, key, callable_value):
        if key in self:
            super().__setitem__(key, callable_value())
    # we need a method to transform back to a dict
    def to_dict(self):
        return dict(self)


data = {
    'a': 1,
    'b': '2',
    'c': 3,
    'd': '4',
    'e': 5
}

template1 = {
    'p':'Nan',
    'q':'Nan',
    'r':'Nan'
}

template2 = {
    'p':'Nan',
    's':'Nan',
    't':'Nan'
}

def func(template,data):
    # create a GuardDict from the dict
    # this will utilize __setitem__ and only actually set keys
    # that already exist in the original dict
    template = GuardDict(template)
    template['p'] = lambda: data['a']
    template['q'] = lambda: data['b'] + 'some manipulation'
    template['r'] = lambda: data['c']
    template['s'] = lambda: data['d'] + 'some mainpulation'
    template['t'] = lambda: data['e']
    # set back to a dict
    return template.to_dict()

template1 = func(template1, data)
template2 = func(template2, data)
print(template1)
print(template2)

我应该注意,如果有其他用户使用您的代码,他们可能会因此讨厌您。

如果您的操作可以表示为一个简单的 lambda,您可以将 condition/assigment 封装在一个函数中以减少代码混乱:

def func(template,data):
    def apply(k,action):
        if k in template: template[k] = action()
    apply('p',lambda: data['a'])
    apply('q',lambda: data['b'][:2] + 'some manipulation')
    apply('r',lambda: data['c'])
    apply('s',lambda: data['d'] + 'some mainpulation')
    apply('t',lambda: data['e'])