Обертки функций внутри класса с использованием декораторов - PullRequest
0 голосов
/ 06 мая 2018

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

следующим образом:

class UserDatabaseManager(object):

    DEFAULT_DB_PATH = 'test.db'

    def __init__(self, dbpath=DEFAULT_DB_PATH):
        dbpath = 'sqlite:///' + dbpath
        self.engine = create_engine(dbpath, echo=True)

    def add_user(self, username, password):
        Session = sessionmaker(bind=self.engine)
        session = Session()
        # <============================== To be wrapped
        user = User(username, password)
        session.add(user)
        # ==============================>
        session.commit()
        session.close()

    def delete_user(self, user):
        Session = sessionmaker(bind=self.engine)
        session = Session()
        # <============================== To be wrapped
        # Delete user here
        # ==============================>
        session.commit()
        session.close()

Что такое идиоматический способ абстрагирования повторных вызовов сеанса с помощью оболочки функций?

Я бы предпочел сделать это с декораторами, объявив закрытый _Decorators класс внутри UserDatabaseManager и реализовав внутри него функцию-оболочку, но тогда такой класс не сможет получить доступ к атрибуту экземпляра self.engine объекта внешний класс.

Ответы [ 2 ]

0 голосов
/ 06 мая 2018

Простой (и, на мой взгляд, самый идиоматичный) способ сделать это состоит в том, чтобы обернуть стандартный шаблон установки / демонтажа в диспетчере контекста , используя contextlib.contextmanager. Затем вы просто используете оператор with в функциях, которые выполняют работу (вместо того, чтобы пытаться обернуть эту функцию самостоятельно).

Например:

from contextlib import contextmanager

class UserDatabaseManager(object):

    DEFAULT_DB_PATH = 'test.db'

    def __init__(self, dbpath=DEFAULT_DB_PATH):
        dbpath = 'sqlite:///' + dbpath
        self.engine = create_engine(dbpath, echo=True)

    @contextmanager
    def session(self):
        try:
            Session = sessionmaker(bind=self.engine)
            session = Session()
            yield session
            session.commit()
        except:
            session.rollback()
        finally:
            session.close()

    def add_user(self, username, password):
        with self.session() as session:
            user = User(username, password)
            session.add(user)

    def delete_user(self, user):
        with self.session() as session:
            session.delete(user)
0 голосов
/ 06 мая 2018

Вы можете создать простую функцию вне класса, чтобы обернуть каждый метод:

def create_session(**kwargs):
   def outer(f):
     def wrapper(cls, *args):
       Session = sessionmaker(bind=getattr(cls, 'engine'))
       session = Session()
       getattr(session, kwargs.get('action', 'add'))(f(cls, *args))
       session.commit()
       session.close()
     return wrapper
   return outer

class UserDatabaseManager(object):
  DEFAULT_DB_PATH = 'test.db'
  def __init__(self, dbpath=DEFAULT_DB_PATH):
    dbpath = 'sqlite:///' + dbpath
    self.engine = create_engine(dbpath, echo=True)
  @create_session(action = 'add')
  def add_user(self, username, password):
    return User(username, password)

  @create_session(action = 'delete')
  def delete_user(self, user):
     return User(username, password)

Как правило, операции настройки и удаления, подобные описанным выше, лучше всего помещать в менеджер контекста:

class UserDatabaseManager(object):
  DEFAULT_DB_PATH = 'test.db'
  def __init__(self, dbpath=DEFAULT_DB_PATH):
     dbpath = 'sqlite:///' + dbpath
     self.engine = create_engine(dbpath, echo=True)

class UserAction(UserDatabaseManager):
  def __init__(self, path):
    UserDatabaseManager.__init__(self, path)
  def __enter__(self):
    self.session = sessionmaker(bind=self.engine)()
    return self.session
  def __exit__(self, *args):
     self.session.commit()
     self.session.close()

with UserAction('/the/path') as action:
   action.add(User(username, password))

with UserAction('/the/path') as action:
   action.remove(User(username, password))
...