【问题标题】:Design of Python conditional importsPython条件导入的设计
【发布时间】:2019-12-24 00:43:53
【问题描述】:

我是 Python 条件导入的新手,我正在考虑两种方法来设计我的模块。我会很感激关于我为什么要选择一个与另一个(或者是否存在更好的替代方案)的意见。

问题

我有一个程序需要在不同条件下调用结构相同但不同的模块。这些模块都具有相同的功能、输入、输出等,唯一的区别在于它们在各自的功能范围内做什么。例如,

# module_A.py
def get_the_thing(input):
    # do the thing specific to module A
    return thing

# module_B.py
def get_the_thing(input):
    # do the thing specific to module B
    return thing

选项 1

根据输入值,我会根据this answer 有条件地导入适当的模块。

if val == 'A':
    import module_A
if val == 'B':
    import module_B

选项 2

我使用输入变量将模块名称生成为字符串,然后使用this method 从基于该字符串的正确模块调用函数。我相信这需要我先导入所有模块。

import module_A
import module_B

in_var = get_input() # Say my input variable is 'A', meaning use Module A
module_nm = 'module_' + in_var
function_nm = 'get_the_thing'

getattr(globals()[module_nm], function_nm)(my_args)

这个想法是通过在运行时生成模块和函数名称来调用 module_A.get_the_thing()。这是一个仅用于一个函数调用的无聊示例,但在我的实际情况下,我将使用函数列表,只是想让事情变得简单。

任何关于哪一种设计更好的想法,或者是否存在优于这两种设计的任何想法?希望有任何理由为什么。当然,A 更简洁,可能更直观,但不确定这一定等同于良好的设计或性能差异。

【问题讨论】:

  • 选项 1 似乎要好得多。 Python 在某些情况下会在加载特定于平台的内容时使用它。我不明白为什么 2 会更受欢迎。就个人而言,我每次看到globals()[] 时都会作呕。我认为我从未见过它用于解决任何问题的最佳选择(并不是说它们不存在,但它们似乎很少见)
  • 要使 1 工作,您可能希望改为使用 if val == 'A': import module_A as some_name; elif val == 'B': import module_B as some_name。如果导入的名称不同,它将无法正常工作,因为您需要稍后找出导入的名称。
  • @Carcigenicate 如果它是from module_A import *from module_B import * 可以吗?然后你可以在不知道模块名称的情况下直接调用get_the_thing()
  • @Barmar 这可行,但import * 不是一个好习惯。 Iirc,PEP8 明确不鼓励它,因为它污染了命名空间。
  • 另一种方法是定义多个类,然后if val == 'A': thing = classA() elif val == 'B': thing = classB()

标签: python design-patterns module


【解决方案1】:

我会选择选项 1。它明显更简洁,而且您不需要摆弄字符串来进行查找。至少,处理字符串会使重构复杂化。如果您更改了任何涉及的名称,您必须记住也要更新字符串;特别是因为即使是智能 IDE 也无法通过典型的 shift+F6 重命名来帮助您。像这样难以维护代码的地方越少越好。

不过,我会对 1 做一个小改动。以您现在的方式,每次使用该模块仍需要使用限定名称,例如module_A.do_thing()。这意味着无论何时你想调用一个函数,你都需要首先弄清楚哪个是首先导入的,这会导致代码更加混乱。我会用一个通用名称导入它们:

if val == 'A':
    import module_A as my_module

if val == 'B':
    import module_B as my_module

. . .

my_module.do_thing()  # The exact function called will depend on which module was imported as my_module

您还可以按照 cmets 中的建议,使用通配符导入来避免需要为模块使用名称:

if val == 'A':
    from module_A import *

if val == 'B':
    from module_B import *

. . .

do_thing()

但是PEP8不鼓励这样做:

应避免使用通配符导入 (from <module> import *),因为它们会使命名空间中存在哪些名称变得不清楚,从而使读者和许多自动化工具感到困惑。

它还会污染您要导入的命名空间,从而更容易从导入的文件中意外隐藏名称。

【讨论】:

    猜你喜欢
    • 2013-06-29
    • 2010-11-22
    • 2017-05-03
    • 2011-03-30
    • 1970-01-01
    • 2017-07-23
    • 1970-01-01
    • 2018-09-18
    • 2021-01-12
    相关资源
    最近更新 更多