通过配置文件创建函数实例

Creating instances of a function via a configuration file

我们有一个配置文件,它被读取并存储为字典,例如

config = {'type': A, 'input': {'color': 'red', 'border': 'thick', 'shape': 'square'}}

现在我们想使用这个配置对象来创建一个 class 的实例,比如说 Drawing_Class,条件是如果类型是 'A',颜色总是蓝色,如果类型为 'B',则形状始终为圆形。

我已经写了一个函数来实现这个看起来非常不符合Python规范,想知道是否有更好的方法来实现我的目标?当然,在我给出的例子中,可以直接在配置文件中输入需要的颜色和形状,但实际问题比较复杂,无法实现。

这是我的函数。

def drawing_dictionary(type):
    if type == 'A':
        value = config['inputs'].pop('color')
        output = drawing_function(**config['inputs'], colour='blue')
        config['input']['color'] = value

    if type == 'B':
        value = config['inputs'].pop('shape')
        output = drawing_function(**config['inputs'], shape='circle')
        config['input']['shape'] = value

    return output

它实现了我需要它实现的目标,即基于此配置文件创建自定义实例,但以一种看起来非常不优雅的方式。将不胜感激。

我问了一个关于 drawing_function 的问题,但我想我会在答案中写下一些内容并根据需要进行更新。

首先不要命名变量类型,因为它不是 python 中的保留关键字,它是一个允许您确定对象类型的内置函数。

其次你说你想创建一个函数的实例,你是在谈论仿函数吗? drawing_function 是 class 吗?还是您只是根据配置文件调用具有不同参数的函数(这就是您的代码似乎表明的以及我所假设的)?

您项目中的其他代码将影响您要使用的参数列表以及您希望如何移动数据以便函数获得正确的参数。您的示例代码看起来像您已将 config 设为全局变量,我不推荐这样做。

如果配置文件中有额外的值或缺少值怎么办?你会导致异常我添加了一个理论验证例程。

对示例代码的一些建议更改:

def drawing_dictionary(config):
    config = validate_config(config) #remove values you don't understand
    #and confirm all the values you need are present set type to 'Error' 
    #if something is wrong
    if config['type'] == 'Error':
        handle_error()#maybe set a default config?
    else: #this assumes you can't fix the config
        return drawing_function(**config['inputs'],config_type=config['type'])

    return some_sort_of_error_value_or_throw_exception

在 drawing_function 中,您将解压缩配置 ['inputs'] 中的所有值,因此您将拥有一个名为 color 的变量和一个名为 shape 的变量,然后您可以按类型处理更改在那个函数中确保它总是发生并且不改变配置字典:

if config_type == 'A':
    colour = 'blue'
elif config_type == 'B':
    shape = 'circle'

如果您使用 python 3.10 或更新版本,您也可以将 if elif 内容换成 a match case statement

我喜欢用 dicts 来代替 potentially-large if-elif 块,所以我要做的第一件事就是为类型名称创建一个查找,包含每种类型的字段名称和默认值。

types = {
    "A": ("color", "blue"),
    "B": ("shape", "circle"),
}

我还建议复制配置 table 的内容,而不是修改它。这样可以防止你不小心修改它,也可以使函数re-entrant。这是我的版本:

def drawing_dictionary(typename):
    key, default = types[typename]
    kwargs = config["inputs"].copy()
    kwargs[key] = default
    return drawing_function(**kwargs)

使用此实现,支持新类型就像在 types 中添加一行一样简单,而不是定义新的 if 块。

您似乎有一个配置层次结构,全局配置“if A then color=blue, if B then shape=circle”优先于配置文件。这样的层次结构可以在 collections.ChainMap 的帮助下建模,请参见以下示例:

from collections import ChainMap


GLOBAL_CONFIG = {
    'A': dict(color='blue'),
    'B': dict(shape='circle'),
}


def drawing_dictionary(config):
    return drawing_function(**ChainMap(
        GLOBAL_CONFIG.get(config['type'], {}),
        config['input'],
    ))


def drawing_function(**kwargs):
    import pprint
    pprint.pprint(kwargs)


drawing_dictionary({'type': 'A', 'input': {'color': 'red', 'border': 'thick', 'shape': 'square'}})
drawing_dictionary({'type': 'B', 'input': {'color': 'red', 'border': 'thick', 'shape': 'square'}})