簡體   English   中英

Python用可變數量的位置args和可選的arg來裝飾方法

[英]Python decorate methods with variable number of positional args and optional arg

我正在使用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裝飾器的一些東西的最佳時間。 所以經過大量的閱讀,像這一系列的博客文章這個夢幻般的答案,我寫了以下裝飾:

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'))一樣運行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個參數。 有人可以指出我的方法中的錯誤嗎?

更改

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

我注意到的第一件事是你正在調用兩次裝飾函數

@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

在第二次通話期間,會議已經結束。 此外,您不需要在裝飾器函數中明確接受thatsession參數,它們已經在argskwargs 看看這個解決方案:

@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塊中,然后即使裝飾函數拋出異常,您也將確保它已關閉

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM