我正在使用SQLalchemy编写我的第一个Python(3.4)应用程序。我有几种方法都有非常相似的模式。他们采用可选参数session
,默认为None
。如果传递session
,则该函数使用该会话,否则它将打开并使用新会话。例如,请考虑以下方法:
def _stocks(self, session=None):
"""Return a list of all stocks in database."""
newsession = False
if not session:
newsession = True
session = self.db.Session()
stocks = [stock.ticker for stock in session.query(Stock).all()]
if newsession:
session.close()
return stocks
所以,作为Python的新手并渴望学习它的所有功能,我认为这就像是学习Python装饰器的最佳时机。所以经过大量的阅读,比如这个this series of blog posts和this非常好的答案,我写了下面的装饰:
from functools import wraps
def session_manager(func):
"""
Manage creation of session for given function.
If a session is passed to the decorated function, it is simply
passed through, otherwise a new session is created. Finally after
execution of decorated function, the new session (if created) is
closed/
"""
@wraps(func)
def inner(that, session=None, *args, **kwargs):
newsession = False
if not session:
newsession = True
session = that.db.Session()
func(that, session, *args, **kwargs)
if newsession:
session.close()
return func(that, session, *args, **kwargs)
return inner
它看起来很棒。原始方法现在简化为:
@session_manager
def _stocks(self, session=None):
"""Return a list of all stocks in database."""
return [stock.ticker for stock in session.query(Stock).all()]
但是,当我将装饰器应用于除了可选session
之外还需要一些位置参数的函数时,我得到一个错误。所以试着写:
@session_manager
def stock_exists(self, ticker, session=None):
"""
Check for existence of stock in database.
Args:
ticker (str): Ticker symbol for a given company's stock.
session (obj, optional): Database session to use. If not
provided, opens, uses and closes a new session.
Returns:
bool: True if stock is in database, False otherwise.
"""
return bool(session.query(Stock)
.filter_by(ticker=ticker)
.count()
)
并且像print(client.manager.stock_exists('AAPL'))
一样运行会为AttributeError
提供以下追溯:
Traceback (most recent call last):
File "C:\Code\development\Pynance\pynance.py", line 33, in <module>
print(client.manager.stock_exists('GPX'))
File "C:\Code\development\Pynance\pynance\decorators.py", line 24, in inner
func(that, session, *args, **kwargs)
File "C:\Code\development\Pynance\pynance\database\database.py", line 186, in stock_exists
.count()
AttributeError: 'NoneType' object has no attribute 'query'
[Finished in 0.7s]
所以我在回溯中猜测,我正在弄乱参数的顺序,但我无法弄清楚如何正确地对它们进行排序。除了session
之外,我还有一些我想要装饰的函数,可以使用0-3个参数。有人可以指出我方法中的错误吗?
答案 0 :(得分:3)
更改
def inner(that, session=None, *args, **kwargs):
到
def inner(that, *args, session=None, **kwargs):
和
return func(that, session, *args, **kwargs)
到
return func(that, *args, session=session, **kwargs)
有效:
def session_manager(func):
def inner(that, *args, session=None, **kwargs):
if not session:
session = object()
return func(that, *args, session=session, **kwargs)
return inner
class A():
@session_manager
def _stocks(self, session=None):
print(session)
return True
@session_manager
def stock_exists(self, ticker, session=None):
print(ticker, session)
return True
a = A()
a._stocks()
a.stock_exists('ticker')
输出:
$ python3 test.py
<object object at 0x7f4197810070>
ticker <object object at 0x7f4197810070>
使用def inner(that, session=None, *args, **kwargs)
时,任何第二个位置参数(计数self
)都被视为session
参数。因此,当您致电manager.stock_exists('AAPL')
session
获取值AAPL
时。
答案 1 :(得分:1)
我注意到的第一件事是你正在调用两次装饰功能
@wraps(func)
def inner(that, session=None, *args, **kwargs):
newsession = False
if not session:
newsession = True
session = that.db.Session()
#calling first time
func(that, session, *args, **kwargs)
if newsession:
session.close()
#calling second time
return func(that, session, *args, **kwargs)
return inner
在第二次通话期间,会议已经关闭。
此外,您无需在装饰器功能中明确接受that
和session
参数,它们已位于args
和kwargs
中。看看这个解决方案:
@wraps(func)
def inner(*args, **kwargs):
session = None
if not 'session' in kwargs:
session = that.db.Session()
kwargs['session'] = session
result = func(*args, **kwargs)
if session:
session.close()
return result
return inner
您可能还希望将会话结束代码放在finally
块中,然后即使装饰函数抛出异常,您也会确定它已关闭