2010-09-15 6 views
22

Предположим, что вам требуется трудоемкая работа, когда сначала импортируется модуль/класс. Эта функциональность зависит от переданной переменной. Это нужно делать только при загрузке модуля/класса. Все экземпляры класса могут затем использовать результат.Переменная перехода при импорте

Например, я использую 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)) 

Am Я застрял создание функции уровня модуля, который я называю, чтобы сделать работу?

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

Я должен здесь что-то упустить. Благодарю.

ответ

19

Наличие функции init модуля не является неслыханным. Pygame делает это для функций инициализации sdl. Так что да, ваш лучший выбор, вероятно,

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

любая информация о том, как настроить функцию инициализации в модуле? это то же самое, что и файл \ _ \ _ init \ _ \ _. py? он не появляется так, основываясь на том, что вы написали выше. возможно ли это, если у вас есть обычные функции в вашем модуле, а не в классах? – user1748155

+5

Это не похоже на файл '__init __. Py'. Функция init в примере представляет собой обычную функцию, которая инициализирует глобальное состояние в модуле. – nmichaels

+0

не удалось найти хорошую ссылку, описывающую метод init. Не могли бы вы поделиться одной ссылкой, описывающей работу модуля init. Благодаря! – NightFurry

2

Нет, вы не застряли в функции уровня модуля, это просто лучший вариант. Вы также можете использовать встроенные декодеры или classmethod, чтобы сделать его методом на someSclass, который можно вызвать до его создания.

Это будет иметь смысл только, если все, кроме someClass, можно использовать без инициализации, и я все еще считаю, что функция уровня модуля лучше.

0

Вы можете воспользоваться прокси-сервером, который осуществляет ленивую загрузку?

Проверьте активное состояние «Lazy Module Imports».

4

Невозможно передать переменную при импорте.

Некоторые идеи:

  • делают модуль получить переменную из модуля вызова с помощью проверки; не очень вещий
  • использовать функцию инициализации для модуля, это лучший способ
2

Пара других вариантов, которые могут достичь своей цели (хотя функция инициализации(), вероятно, уборщик):

  • Используйте переменную окружения
  • Используйте отдельный модуль M, чтобы удерживать эту переменную, заданную импортером. Затем импортированный модуль может знать, где найти M, или может полагаться на sys.meta_path для его получения.
11

Мне нужно было сделать что-то подобное для моего проекта. Если вы не хотите полагаться на вызывающий скрипт для запуска функции инициализации, вы можете добавить свой собственный встроенный Python, который затем доступен для всех модулей во время выполнения.

Будьте осторожны, чтобы назвать свое встроенное нечто уникальное, что вряд ли вызовет столкновение пространства имен (например, myapp_myvarname).

перспективе.ру

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__ не установлена, она будет использовать значение по умолчанию.

Редактировать: Некоторые считают это примером «Обезглавливания обезьян». Для более элегантного решения без патча обезьяны, see my other answer.

+1

Спасибо! Это полезно для модулей, которые выполняют код ** во время импорта **, и вы хотите установить переменные, необходимые для этого выполнения. Функция 'init' не является полезной в этом случае, так как' init' может быть вызван только после завершения импорта. – ADTC

1

Есть два решения, о которых я могу думать, оба из которых являются очень похожими на работу решениями. Первый заключается в канаву импорта и запустить свой сценарий как этот

sys.argv [1] = "argument 1" 
sys.argv [2] = "argument 2" 
execfile("/path/to/dependency.py") #depreciated in python 3.x 

второй должен поставить свои аргументы во внешний временный файл, а затем прочитать из этого файла в зависимости.

3

Если есть только один элемент конфигурации для установки, то я нашел переопределение python __builtin__, чтобы работать нормально, но это пример "Monkey patching", на который нахмурились некоторые.

Более чистый способ сделать это, который очень полезен для нескольких элементов конфигурации в вашем проекте, - создать отдельный модуль конфигурации, который сначала импортируется вашим кодом упаковки, а элементы, установленные во время выполнения, до того, как ваш функциональный модуль импортирует его , Этот шаблон часто используется в других проектах.

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 
+1

Это тоже очень приятно! Много элегантности. Никаких обезьян.Надеюсь, вы не возражаете, что я отредактировал ваш другой ответ, чтобы указать ссылку на этот. – ADTC

+0

Я хотел бы добавить, что вы можете просто создавать файлы, называемые 'myconfig.py' и' mymodule.py'. Это будет работать одинаково с меньшей сложностью. ** Интересные факты: ** У вас может быть 'myconfig.py' полностью пустым. Чтобы проверить, установлена ​​ли переменная, вы также можете использовать 'try' с' except AttributeError' (если вам не нравится использовать try-except для такого рода вещей). – ADTC