class 内的函数包装器使用装饰器
Function wrappers inside class using decorators
我有一个与数据库交互的 class,因此在 class 的每个成员方法之前和之后都有重复的操作(建立会话、提交、关闭会话)。
如下:
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()
使用函数包装器抽象出重复会话调用的惯用方法是什么?
我更愿意通过在 UserDatabaseManager
中声明一个私有的 _Decorators
class 并在其中实现包装函数来使用装饰器来做到这一点,但是这样 class 赢了无法访问外部 class.
的 self.engine
实例属性
您可以在 class 之外创建一个简单的函数来包装每个方法:
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))
一个简单的(在我看来,最惯用的)方法是将 setup/teardown 样板代码包装在执行工作的函数中的 context manager using contextlib.contextmanager
. You then simply use a 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)
我有一个与数据库交互的 class,因此在 class 的每个成员方法之前和之后都有重复的操作(建立会话、提交、关闭会话)。
如下:
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()
使用函数包装器抽象出重复会话调用的惯用方法是什么?
我更愿意通过在 UserDatabaseManager
中声明一个私有的 _Decorators
class 并在其中实现包装函数来使用装饰器来做到这一点,但是这样 class 赢了无法访问外部 class.
self.engine
实例属性
您可以在 class 之外创建一个简单的函数来包装每个方法:
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))
一个简单的(在我看来,最惯用的)方法是将 setup/teardown 样板代码包装在执行工作的函数中的 context manager using contextlib.contextmanager
. You then simply use a 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)