进行变量导入时避免使用环境变量

时间:2019-07-07 11:02:07

标签: python environment-variables directory-structure

我一直在与一个项目一起工作,我可以根据他们在this project中的工作选择使用两个后端(假设后端1和2)。但是,该项目依靠已经定义的环境变量来确定在执行代码之前要使用哪个后端。我编写的代码不是这种情况。

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

Project

我考虑过直接在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

我想用其他任何方法减少最后一个环境变量,但我不知道该怎么用。

1 个答案:

答案 0 :(得分:1)

就像@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))

缺点是它有点隐含。