首页 > 解决方案 > 进行变量导入时避免使用环境变量

问题描述

我一直在处理一个项目,我可以选择使用两个后端(假设是后端 1 和 2,让我们说)按照他们在这个项目中所做的事情。但是,该项目依靠已经定义的环境变量来决定在执行代码之前使用哪个后端。我正在编写的代码并非如此。

我想知道在这种情况下是否有任何替代方法可以使用环境变量,以便在执行时,我可以根据变量的值加载一个或另一个后端。我的项目的一般结构如下:

项目

曾想过直接在 python 代码中直接设置环境变量os.environ['NAME_OF_ENV_VARIABLE'] = 'BACKEND 1'(鉴于这种需要,我想知道是否可以有某种变量跨越不同的文件,以便在我导入模块时,文件可以在后端之间消除歧义。__init__.py

PS:也许我正在做的事情毫无意义。


[更新]有关该问题的更多信息,已简化为最小扩展。我的主文件处理一些数据,如下所示:

from argparse import ArgumentParser
from utils.loader import load_data
from utils.do import do_stuff

def main(config):
    data = load_data(config)
    do_stuff(config, data)

if __name__ == '__main__':
    # Retrieve input data
    parser = ArgumentParser()
    parser.add_argument('--backend', type=str, default='backend 1', help='backend to use')
    inputs = parser.parse_args()

    config = "backend 1" if inputs.backend == "1" else "backend 2"

    # Call main function
    main(config)

我猜数据加载器load_data(config)对此并不重要。然后,包含的文件do_stuff(data)如下:

import backend

def do_stuff(config, data):
    # Do some really important stuff that is coded in backend 1 and backend 2
    a = backend.do_something(data)
    print(a)

它只是加载后端(!!!)并做一些事情。该do_stuff(data)函数本身在后端 1 或后端 2 中执行一些编码:

def do_something(data):
    data.values = "Value obtained through functions of 'BACKEND 1' (same function names and inputs, different backends used)"

def do_something(data):
    data.values = "Value obtained through functions of 'BACKEND 2' (same function names and inputs, different backends used)"

最后,后端模块本身具有以下__init__.py文件:

from .load_backend import do_something

从文件中加载,它只是在给定环境变量的load_backend.py情况下消除后端的歧义:

from __future__ import absolute_import
from __future__ import print_function
import os
import sys

# Default backend: backend 1
if 'ENVIRONMENT_VARIABLE' in os.environ:
    _BACKEND = os.environ['ENVIRONMENT_VARIABLE']
else:
    _BACKEND = 'backend 1'

# Import backend functions.
if _BACKEND == "backend 1":
    sys.stderr.write('Using backend 1\n')
    from .backend_1 import *
elif _BACKEND == "backend 2":
    sys.stderr.write('Using backend 2\n')
    from .backend_2 import *
else:
    raise ValueError('Unable to import backend : ' + str(_BACKEND))


def backend():
    """Publicly accessible method
    for determining the current backend.
    # Returns
        String, the name of the backend
    # Example
    ```python
        >>> backend.backend()
        'backend 1'
    ```
    """
    return _BACKEND

我想要的是用其他任何东西来减少最后一个环境变量,但我不知道我可以使用什么。

标签: pythonenvironment-variablesdirectory-structure

解决方案


就像@DanielRoseman 问的那样,我只会传递后端参数。例如load_backend,在尽可能少地更改代码的同时:

from __future__ import absolute_import
from __future__ import print_function
import os
import sys

def backend(backend):
    """Returns the wanted backend module"""
    # Import backend functions.
    if backend == "backend 1":
        sys.stderr.write('Using backend 1\n')
        from . import backend_1 as backend_module
    elif backend == "backend 2":
        sys.stderr.write('Using backend 2\n')
        from . import backend_2 as backend_module
    else:
        raise ValueError('Unable to import backend : ' + str(_BACKEND))

    return backend_module

一个改进可能是用于importlib动态导入后端并将魔术字符串移动到一个常量:

...
import importlib

BACKENDS = {
    "backend 1": "backend_1",
    "backend 2": "backend_2"
}

def load_backend(backend):
    try:
        module = importlib.import_module(
            BACKENDS[backend]
        )
    except KeyError:
        raise ImportError('Unable to import backend : %s' % backend)

    sys.stderr.write('Using %s\n' % backend)
    return module

因此,您可以在do_stuff文件中执行此操作:

import load_backend

def do_stuff(config, data):
    # Do some really important stuff that is coded in backend 1 and backend 2
    backend = load_backend.backend(config)
    a = backend.do_something(data)
    print(a)

另一种解决方法是使用单例模式,您可以在其中设置一次后端变量(以及您希望广泛使用的其他设置):

在一个settings.py或任何地方:

class SettingSingleton(object):
    _backend = None

    def __new__(cls, backend=None, *args, **kwargs):
        cls._backend = cls._backend or backend
        return super(SettingsSingleton, cls).__new__(cls, *args, **kwargs)

    @property
    def backend(self):
        return self._backend

您可以在主中初始化它。

from argparse import ArgumentParser
from utils.loader import load_data
from utils.do import do_stuff
from settings import SettingSingleton


def main(config):
    SettingsSingleton(backend=config)
    data = load_data(config)
    do_stuff(config, data)

...

现在您可以执行以下操作:

from __future__ import absolute_import
from __future__ import print_function
import os
import sys

from settings import SettingsSingleton

_BACKEND = SettingsSingleton().backend

# Import backend functions.
if _BACKEND == "backend 1":
    sys.stderr.write('Using backend 1\n')
    from .backend_1 import *
elif _BACKEND == "backend 2":
    sys.stderr.write('Using backend 2\n')
    from .backend_2 import *
else:
    raise ValueError('Unable to import backend : ' + str(_BACKEND))

这样做的缺点是它有点隐含。


推荐阅读