2012-05-30 3 views
1

Я пытаюсь выяснить, как я могу переопределить значения по умолчанию для функций, определенных внутри некоторого модуля. Рассмотрим этот код (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 внутри functions, а 2 является результатом моего повторного импорта my_settings.

Есть ли способ переопределить настройки по умолчанию, которые необходимы dummy_function, просто используя my_settings? Я думал об уходе из линии from default_settings import * в functions, но затем я столкнулся с NameError. Есть ли способ импорта из functions и в то же время передать все переменные в functions?

+7

Это сообщение может быть лучшим аргументом, который я когда-либо читал, никогда не используя 'from foo import *'. – geoffspear

+0

Я использую 'from foo import *' или 'from foo import DO_DEBUG, SOME_DEFAULT', не имеет большого значения, не так ли? Я буду рад услышать от вас, как поступать по-другому. – memyself

ответ

4

Вам необходимо инкапсулировать свои настройки по-разному. Прямо сейчас вы используете два разных модуля в качестве контейнеров для двух разных наборов настроек. Затем вы импортируете все имена из этих модулей, считая from my_settings import *, чтобы переписать имена, импортированные from default_settings import *. Это злоупотребление import.

В целом, я бы сказал, что имена, определенные при import, должны быть неявным образом переопределены модулем , а не. from module import * уже плохо, потому что он неявно определяет кучу имен в глобальном пространстве имен; используя другой* импорт неявно redefine эти имена просто страшны.

Мое предложение было бы либо использовать словарь для хранения настроек, либо использовать класс настроек. В первом случае, вы могли бы сделать что-то вроде этого:

# 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 

И так далее.

+0

Вы меня заставили: «вам нужно инкапсулировать ваши настройки по-другому» (+1) – mgilson

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. Я думаю, что ваш код может выиграть от какой-то реструктуризации.

+0

Не уверен, что вы имеете в виду о 'myfunction.py'. Независимо от этого, немного более чистый способ сделать что-то вроде этого может состоять в том, чтобы модуль 'default_settings' предоставлял функцию для изменения некоторых или всех своих значений по умолчанию. Если это сделать до того, как другие модули импортируют и используют его, тогда они получат перенастроенные значения, потому что Python кэширует модули и на самом деле не перезагружает и не повторно инициализирует его. – martineau

+0

@martineau: хороший комментарий. Я не уверен, что я имел в виду, например, 'myfunction.py' - должен быть' functions.py'. И я подумал о том, что функции 'default_settings' также будут перезагружать эти значения, но проблема с этим кодом, который кажется грязным, заключается в том, что вам нужно установить эти константы * перед импортом чего-либо еще * из-за использования декораторов. Лучшим решением было бы иметь фабрику в 'functions.py', которая возвращает украшенную или не декорированную версию функции на основе флага - затем используйте фабрику, чтобы получить функцию ... – mgilson

+0

@mgilson Мне нравится ваше решение. Однако я вижу проблему с наличием более чем нескольких настроек - это немного больно, чтобы вручную добавлять эти строки за строкой. Есть ли лучший способ переписать все определения, найденные в 'my_settings' (не все из них могут быть определены, если пользователь решит придерживаться определенного значения по умолчанию) автоматически? – memyself

Смежные вопросы