简体   繁体   English

条件导入的良好做法

[英]Good practice with conditional imports

I have a module for configuration, projectConfig , to parse a sample sheet for a project: 我有一个配置模块projectConfig ,用于解析项目的示例表:

class SampleSheetFields():
    FIELD_1 = "field1"
    FIELD_2 = "field2"


class SampleSheetFieldsOld():
    FIELD_1 = "field_1"
    FIELD_2 = "field_2"

I had been using the first class in other modules like this: 我一直在使用其他模块中的第一个类,如下所示:

from projectConfig import SampleSheetFields as ssFields

class SomeClass

    def __init__(self):
        ...
        check(someContent, ssFields.FIELD_1)

The thing is I had developed my software using the reference to ssFields quite a lot. 问题是我使用对ssFields的引用开发了我的软件。 At some point new specifications said that the software should also use sample sheets with different field names. 在某些时候,新的规范说软件也应该使用具有不同字段名称的样本表。 The quickest way I found to achieve that, without messing too much with the code, was to add the class SampleSheetFieldsOld in the projectConfig and to make a conditional import in my modules: 我发现实现这一目标的最快捷的方法是在SampleSheetFieldsOld代码进行过多处理的情况下,在projectConfig添加类SampleSheetFieldsOld并在我的模块中进行条件导入:

class SomeClass:

    def __init__(self, useOld):
        if useOld:
            from projectConfig import SampleSheetFieldsOld as ssFields
        else:
            from projectConfig import SampleSheetFields as ssFields

        ...
        check(someContent, ssFields.FIELD_1)

Note that the mandatory fields which are used have the same name so there is no conflicting or missing field. 请注意,使用的必填字段具有相同的名称,因此没有冲突或缺少字段。 The program works as expected. 该程序按预期工作。

My questions are: 我的问题是:

  1. How bad is this practice, if it is bad; 这种做法有多糟糕,如果不好的话; and
  2. How can I circumvent it to make a better and more sustainable code? 我如何规避它以制作更好,更可持续的代码?

It's probably not the worst thing, but what I do find kind of problematic is the fact that you're now locked into two configuration options, old and new. 这可能不是最糟糕的事情,但我发现有些问题是你现在被锁定为两个配置选项,无论是新的还是新的。 What if you need to add a third or fourth etc. set someday? 如果你有一天需要添加第三个或第四个等等怎么办? You won't be able to use a simple boolean test anymore. 您将无法再使用简单的布尔测试。

Besides, it looks like your configuration options are all just simple string values, accessible via string keys. 此外,您的配置选项看起来都只是简单的字符串值,可通过字符串键访问。 You don't need a class for that. 你不需要一个班级。

My suggestion is to forget about doing your configuration with the source code and use a configuration file . 我的建议是忘记使用源代码进行配置并使用配置文件 In your projectConfig you can have a dict which you initialize from a file, whose path/name can be provided on the command line or in whatever way is convenient. 在您的projectConfig您可以拥有一个从文件初始化的dict ,其路径/名称可以在命令行上提供,也可以以任何方便的方式提供。 So projectConfig.py might go something like this: 所以projectConfig.py可能会是这样的:

config_options = {}

def load_configuration(filename):
    with open(filename) as f:
        for line in f:
            # get key and value
            config_options[key] = value

Then everywhere you need to get a field name, just access projectConfig.config_options['field_key'] , eg 然后你需要获得一个字段名称,只需访问projectConfig.config_options['field_key'] ,例如

from projectConfig import config_options

class SomeClass

    def __init__(self):
        ...
        check(someContent, config_options['FIELD_1'])

Or use dict.get(key, default) if there is a reasonable default value. 如果有合理的默认值dict.get(key, default)使用dict.get(key, default) This way, each time you need to switch to a different set of field names, you just create a new configuration file, and you don't have to touch the code. 这样,每次需要切换到一组不同的字段名称时,只需创建一个新的配置文件,而不必触摸代码。

Python's standard library includes a configparser module which can handle the loading for you. Python的标准库包含一个configparser模块 ,可以为您处理加载。

If you only need the class attributes, you could create a class factory, using type to create new classes, like: 如果只需要类属性,则可以创建类工厂,使用type创建新类,如:

FIELDS = dict(
    new=dict(FIELD_1="field1", FIELD_2="field2"),
    old=dict(FIELD_1="field_1", FIELD_2="field_2"),
}


def sample_sheet_field_factory(field_spec='new'):
    return type("SampleSheetFields", (object,), FIELDS[field_spec])

This can easily be extended to further sets of field specifications, and doesn't require a conditional import: 这可以很容易地扩展到更多的字段规范集,并且不需要条件导入:

from wherever import sample_sheet_field_factory

class SomeClass(object):

    def __init__(self, use_old):
        ss_fields = sample_sheet_field_factory("old" if use_old else "new")
        check(some_content, ss_fields.FIELD_1)

You could also use a namedtuple , rather than a class, which would be a little more lightweight. 你也可以使用一个namedtuple而不是一个类,它会更轻一些。 Note edits for compliance with the style guide . 注意编辑符合样式指南

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM