如何覆盖模块中函数所需的默认值?

时间:2012-05-30 14:26:24

标签: python

我试图找出如何覆盖某些模块中定义的函数的默认值。请考虑此代码(program.py):

# import the default & user defined settings
from default_settings import *
from my_settings import *

# import some functions, which might be dependent on the settings above
from functions import *

# call dummy_function from 'functions' - prints '1'
dummy_function()

# output SOME_DEFAULT - will be '1'
print SOME_DEFAULT

# re-import 'my_settings' - SOME_DEFAULT will now be '2'
from my_settings import *
print SOME_DEFAULT

这里是default_settings.py

DO_DEBUG = False
SOME_DEFAULT = 1

这里是my_settings.py,我想在functions.py内使用哪些值:

DO_DEBUG = True
SOME_DEFAULT = 2

这是functions.py,我需要导入default_settings,否则我会得到一个NameError。我不想在此处导入my_settings,因为functions.py应该更像是通用库。

# if I leave this line out, then I get a 
# "NameError: name 'SOME_DEFAULT' is not defined"
from default_settings import *

# I don't want to add "from my_settings import *" here, because 'functions.py' 
# is supposed to be a generic library of functions.

# dummy decorator.
def do_profile(cond):
    def resdec(f):
        if cond:
            print "profiling!"
        return f
    return resdec


# dummy function depending on both 'DO_DEBUG' and 'SOME_DEFAULT'
@do_profile(DO_DEBUG)
def dummy_function(bla=SOME_DEFAULT):
    print bla

如果我运行python program.py,我会得到以下输出:

1
1
2

这是预期的。第一个1来自dummy_function,第二个1来自default_settings导入functions2来自我my_settings重新导入dummy_function

有没有办法可以通过简单地使用my_settings来覆盖from default_settings import *所需的默认设置?我考虑过遗漏functions中的NameError行,但后来我遇到functions。有没有办法从functions导入,同时将所有变量传递到{{1}}?

2 个答案:

答案 0 :(得分:5)

您需要以不同方式封装您的设置。现在,您使用两个不同的模块作为容器,用于两组不同的设置。然后导入这些模块中的所有名称,依靠from my_settings import *覆盖由from default_settings import *导入的名称。这是对import的滥用。

一般情况下,我会说,当import模块 时,定义的名称应该隐式重新定义。 from module import *已经很糟糕,因为它在全局命名空间中隐式定义了一堆名称;使用另一个 *导入隐式重新定义这些名称只是可怕的。

我的建议是使用字典存储设置,或使用设置类。在第一种情况下,你可以这样做:

# settings.py
default_settings = {'foo': True, 'bar': False}
my_settings = {'foo': False}
current_settings = default_settings.copy()
current_settings.update(my_settings)

现在任何模块都可以import settings并像这样访问它们:

foo = settings.default_settings['foo']
bar = settings.current_settings['bar']
settings.current_settings['bar'] = True

对于已导入settings的所有模块,可以看到对这些设置的任何更改。

更复杂的方法可能是使用Settings类。 Settings会定义一些默认值:

class Settings(object):
    def __init__(self, foo=None, bar=None):
        self.foo = foo if foo is not None else True
        self.bar = bar if bar is not None else False

现在您可以创建各种自定义设置:

# settings.py
default_settings = Settings()
my_settings = Settings(foo=False)
current_settings = my_settings.copy()
current_settings.foo = False    # pointless example

同样,如上所述,我们import settings可以访问它们或进行更改:

# foo.py
import settings
bar = settings.current_settings.bar
settings.current_settings.foo = True

您甚至可以从Settings继承以创建新的默认值:

class LocalSettings(Settings):
    def __init__(self, foo=None, bar=None):             # in Python 3, 
        super(LocalSettings, self).__init__(foo, bar)   # super().__... works
        self.foo = foo if foo is not None else True

等等。

答案 1 :(得分:2)

您的功能在导入时在functions.py中定义 - 因此,如果(在program.py中)您

#start of file DO NOT "import functions" YET!!!
import default_settings
import my_settings

default_settings.DO_DEBUG=my_settings.DO_DEBUG
default_settings.SOME_DEFAULT=my_settings.SOME_DEFAULT

import functions 

然后my_settings的设置应该接管。我不知道这是否是你正在寻找的解决方案(它不适合我),但我看不到这个代码结构的任何其他选项。

修改

为了减轻手动重置所有设置的痛苦,您可以使用inspect模块:

#start of file DO NOT "import functions" YET!!!
import default_settings
import my_settings
import inspect

#pull out all of "my_settings" and apply them to "default_settings",
# but only if they don't start with an underscore (reserved)
members=inspect.getmembers(my_settings)
for k,v in members:
    if( not k.startswith('_') ):
        setattr(default_settings,k,getattr(my_settings,k))

import functions

然而,这仍然不适合我 - 我不喜欢的是functions的行为取决于你导入它的时间,这不是你通常在python中看到的。我认为您的代码可以从某种重组中受益。