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

2024-04-25 03:35:47 发布

您现在位置:Python中文网/ 问答频道 /正文

我一直在处理一个项目,我可以根据它们在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

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


Tags: 文件fromimportconfigbackenddataifos
1条回答
网友
1楼 · 发布于 2024-04-25 03:35:47

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

这样做的缺点是有点含蓄。你知道吗

相关问题 更多 >