【问题标题】:Python call a package submodule with variable module namePython调用具有可变模块名称的包子模块
【发布时间】:2014-08-16 07:32:09
【问题描述】:

如何调用公共子模块(存在于各种模块中),从值中选择正确的模块?

例子:

假设我有这个文件夹结构:

myprogram/
          myprogram.py          #main program
          colors/               #colors package
                 __init__.py    #contains __all__
                 blue/          #blue module
                      paint.py  #submodule
                 red/           #red module
                      paint.py  #submodule

paint 子模块在每个模块中具有相同的名称,但每个模块中的代码不同。

在 myprogram.py 我想做这样的事情:

import colors   #import all the colors modules

my_shape = circle()   
my_color = "blue"     #define the module to call by his name
if my_color in colors:
  colors.my_color.paint(my_shape)    #calls the submodule from the right mosule

我希望“颜色”包具有可扩展性,这样我就可以轻松地从一次部署中删除一种颜色。

(我可以制作一个带有内部案例的paint.py模块,但它不容易扩展)

问题是:

  • 可以做if my_module in package的事情吗?
  • 如果是这样,我可以使用变量package.my_module_variable.function() 调用包的模块吗?

我正在尝试解决这个问题storing functions into dicts,我的方法是否正确?

【问题讨论】:

  • 您没有提供有关paint.py 工作原理的详细信息,但我会说拥有一个包含不同颜色或可以用所选颜色实例化的paint.py 更有意义。您可以使用dict 代替paint 中的大“如果”,它应该可以很好地扩展。这是假设颜色表现相同。 IIRC,您可以在包中搜索模块,存储它然后调用它,但这是一种矫枉过正/不优雅的方式,恕我直言。
  • 你可以看看这个关于dynamic module import的相关SO问题

标签: python data-structures scalability packages


【解决方案1】:

您应该能够将颜色子模块视为属性。子模块在其目录中需要__init__.py 文件(例如blue/__init__.py)以及paint.py 模块。然后你可以这样做:

my_color = "blue"
if hasattr(colors, my_color):
    getattr(colors, my_color).paint(my_shape)

但是请注意,在您的示例中,paint 是一个模块,因此您将无法调用该模块。如果paint 模块中有paint 函数,那么你会这样做:

getattr(colors, my_color).paint.paint(my_shape)

编辑:我应该像其他人一样提到,对于您展示的示例而言,这似乎有点过头了。如果你的真实代码/情况比这更复杂,那就去吧。否则,一个不错的选择可能是拥有一个带有用于各种操作的字典的绘图模块。所以像colors/paint.py 这样的文件可能包含:

def paint_blue(shape):
    print("BLUE!")

def paint_red(shape):
    print("RED!")

paint_funcs = {
    "blue": paint_blue,
    "red": paint_red,
}

def paint(color, shape):
    return paint_funcs[color](shape)

编辑 2

需要将color目录下的__init__.py文件作为子包处理,否则无法导入。除非主包知道子模块,否则我使用此类属性的初始方法将不起作用。比如:

import colors
# colors doesn't know it has submodules 'blue' and 'red'
from colors import *
# if the colors __init__ has an __all__ declaration now it does know about them
hasattr(colors, "blue") # will work

或者你可以在颜色__init__.py模块中导入颜色

# in colors/__init__.py
import blue
import red

或者您可以按照其他人所说的动态导入。

【讨论】:

  • 我们如何将模块视为属性?我不确定添加__init__ 是否会将蓝色视为属性... :)
  • 模块只是一个对象,它具有与任何其他属性一样的属性。您可以使用time 之类的任何模块进行测试,试试import time; hasattr(time, "sleep")。子目录中的__init__.py 使颜色文件夹成为子包。否则无法导入。
  • 从头开始,时间模块示例之所以有效,是因为它具有函数sleep。我将在一秒钟内编辑我的答案,以展示我是如何让它发挥作用的。
  • 成功了,谢谢! P.S:在我的情况下,paint.py 子模块会很复杂,但您的第一次编辑也可以;)。
【解决方案2】:

您可以使用imp 模块动态加载模块,前提是您知道模块文件的路径:

import imp

my_color = "blue"
paint = imp.load_source('paint', 'colors/'+my_color+'/paint.py')

paint 可以用作常规模块,就像它是通过以下方式导入的:

from colors import blue.paint as paint

【讨论】:

    猜你喜欢
    • 2012-05-17
    • 2020-10-23
    • 2015-11-23
    • 2018-09-04
    • 1970-01-01
    • 2011-09-17
    • 2023-03-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多