我有一个与数据库交互的类,因此在类的每个成员方法之前和之后都有重复的操作(建立会话,提交,关闭会话)。
如下:
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
类并在其中实现包装函数来对装饰器执行此操作,但是这样的类无法访问{ {1}}外部类的实例属性。
答案 0 :(得分:1)
您可以在类外创建一个简单的函数来包装每个方法:
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))
答案 1 :(得分:1)
一个简单的(在我看来,最常用的)方法是使用context manager将设置/拆卸样板代码包装在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)