导入时传递变量

45 投票
9 回答
51185 浏览
提问于 2025-04-16 04:11

假设你在导入一个模块或类的时候,有一些耗时的工作需要完成。这项工作依赖于一个传入的变量。这个工作只需要在模块或类被加载的时候做一次。之后,这个类的所有实例都可以使用这个结果。

比如,我在使用 rpy2:

import rpy2.robjects as robjects

PATH_TO_R_SOURCE = ## I need to pass this
robjects.r.source(PATH_TO_R_SOURCE, chdir = True) ## this takes time

class SomeClass:
  def __init__(self, aCurve):
    self._curve = aCurve

  def processCurve(self):
    robjects.r['someRFunc'](robjects.FloatVector(self._curve))

我是不是只能创建一个模块级别的函数来完成这项工作呢?

import someClass
someClass.sourceRStuff(PATH_TO_R_SOURCE)
x = someClass.SomeClass([1,2,3,4])
etc...

9 个回答

19

如果只有一个配置项需要设置,我发现重写 Python 的 __builtin__ 是可以正常工作的,但这被称为“猴子补丁”,有些人对此并不赞成。

如果你的项目有多个配置项,使用一个更干净的方法会更有帮助,那就是创建一个单独的配置模块,然后在你的包装代码中先导入这个模块,并在运行时设置这些项,之后再让功能模块导入它。这种模式在其他项目中也经常使用。

myconfig/__init__.py :

PATH_TO_R_SOURCE   = '/default/R/source/path'
OTHER_CONFIG_ITEM  = 'DEFAULT'
PI                 = 3.14

mymodule/__init__.py :

import myconfig

PATH_TO_R_SOURCE = myconfig.PATH_TO_R_SOURCE
robjects.r.source(PATH_TO_R_SOURCE, chdir = True) ## this takes time

class SomeClass:
  def __init__(self, aCurve):
    self._curve = aCurve

if myconfig.VERSION is not None:
  version = myconfig.VERSION
else:
  version = "UNDEFINED"

two_pi = myconfig.PI * 2

你可以在运行时通过包装器改变模块的行为:

run.py :

import myconfig

myconfig.PATH_TO_R_SOURCE = 'actual/path/to/R/source'
myconfig.PI = 3.14159
# we can even add a new configuration item that isn't present in the original myconfig:
myconfig.VERSION="1.0"

import mymodule
print "Mymodule.two_pi = %r" % mymodule.two_pi
print "Mymodule.version is %s" % mymodule.version

输出:

> Mymodule.two_pi = 6.28318
> Mymodule.version is 1.0
27

我在我的项目中也做过类似的事情。如果你不想依赖调用脚本来运行初始化函数,可以添加你自己的Python内置函数,这样在运行时所有模块都可以使用它。

要注意给你的内置函数起个独特的名字,这样就不容易和其他名字冲突(比如 myapp_myvarname)。

run.py

import __builtin__
__builtin__.myapp_PATH_TO_R_SOURCE = 'path.to.r.source'
import someClass

someClass模块 .py

import rpy2.robjects as robjects
import __builtin__

if hasattr(__builtin__, "myapp_PATH_TO_R_SOURCE"):
    PATH_TO_R_SOURCE = __builtin__.myapp_PATH_TO_R_SOURCE
else:
    PATH_TO_R_SOURCE = ## Some default value or Null for Exception handling
robjects.r.source(PATH_TO_R_SOURCE, chdir = True)

...

这种方法适合那些可能有默认值,但你希望在导入时可以覆盖的变量。如果 __builtin__ 变量没有设置,它就会使用默认值。

补充:有些人认为这是一种“猴子补丁”的例子。如果你想要一个更优雅的解决方案,不用猴子补丁,可以看看我的其他回答

37

有一个模块初始化函数并不是新鲜事。比如,Pygame 就是用这种方式来处理 SDL 初始化的函数。所以,没错,你最好的选择可能是

import someModule
someModule.init(NECESSARY_DATA)
x = someModule.someClass(range(1, 5))

撰写回答