2015-03-12 2 views
2

Я работаю над приложением на Python 3, и то, что я делаю, является нетрадиционным.Python Import Module от Decorator

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

class Loader(): 
    def load_cx_oracle(func): 
     def inner(*args, **kwargs): 

      # Additional code before import. 

      import cx_Oracle 

      return func(*args, **kwargs) 
     return inner 

    @load_cx_oracle 
    def function_using_cx_oracle(self): 
     conn = cx_Oracle.connect() 

Однако, когда я пытаюсь выше, я получаю NameError: name 'cx_Oracle' is not defined

ответ

3

import только связывает имя модуля в пространстве имен, в котором вы его используете. если вы выполните import cx_Oracle внутри функции, то имя cx_Oracle будет доступно только внутри этой функции.

Для того, чтобы сделать назначение глобальным, вы можете использовать global. Меняйте декоратор использовать:

global cx_Oracle 
import cx_Oracle 

ли этот подход действительно правильным является дискуссионным. В зависимости от того, как используется ваш код, может быть проще иметь функцию, которую пользователи звонят, если они хотят сделать cx_Oracle доступным, чтобы использовать импортированный try-wrap импорт, как предлагает Даниэль, или чтобы загрузка была определена некоторыми внешняя настройка (например, файл конфигурации).

+0

Это решает мою проблему. Я надеялся пойти с декоратором для этих функций по нескольким причинам. В моем примере отсутствует некоторые элементы конфигурации. Это приложение иногда запускает одну из этих функций. В основном это будут переданные аргументы для запуска только одной функции. – user2004245

+0

Как упоминает @Dunes, использование 'global' работает только в том случае, если декоратор определен в том же модуле, что и декорированные функции. –

0

Если вы хотите дополнительный импорт, используйте примерочных кроме в начале вашего модуля:

try: 
    import cx_Oracle 
except ImportError: 
    cx_Oracle = None 
4

Есть пара проблем с принятым ответом. Наиболее важным из них является то, что он запускает логику импорта каждый раз, когда вызывается функция. Во-вторых, декоратор должен быть определен в том же модуле, который используется в противном случае декоратором, а украшенные будут иметь разные глобальные значения. Вы можете напрямую обращаться к глобальным функциям функции через атрибут __globals__ функции. Пример кода сначала проверяет, существует ли модуль во всех глобальных функциях перед выполнением логики импорта. В этом примере также используется декоратор functools.wraps для сохранения строк документа, имени функции и имен аргументов при использовании таких вещей, как help(func).

from functools import wraps 

def load_operator(func): 
    @wraps(func) 
    def wrapper(*args, **kwargs): 
     if "operator" not in func.__globals__: 
      # setup logic -- only executed once 
      import operator 
      func.__globals__["operator"] = operator 
     return func(*args, **kwargs) 
    return wrapper 

class A: 
    @load_operator 
    def add(self, x, y): 
     return operator.add(x, y) 

    def subtract(self, x, y): 
     return operator.subtract(x, y) 

a = A() 
try: 
    a.subtract(1, 2) 
except NameError: 
    print("operator not yet loaded") 
print(a.add(1, 2)) 
print(A.add) 
+0

Будет ли это также предотвращать множественный импорт из нескольких вызовов функций. Например, если вы включили свой декоратор для 'def subtract (self, x, y)', он будет загружаться только один раз или отдельно для каждой функции? Я понимаю, что текущий ответ действительно вызывает каждую функцию, но мне было интересно, если вы можете изменить их, чтобы предотвратить это через функции в одном модуле. – user2004245

+0

Да, это предотвратит несколько импорта - поскольку функции, объявленные в одном модуле, имеют одни и те же глобальные значения. – Dunes

+1

Запуск логики импорта каждый раз является не чем иным, как тем, что вы делаете, проверяя, что «operater» уже находится в глобальном масштабе - первое, что проверит импорт, - это если модуль уже был импортирован, и в этом случае это простое имя назначение. Однако ваш второй вопрос - это реальная проблема - так +1 для этого. –