Piszę moją pierwszą aplikację Python (3.4) za pomocą SQLalchemy. Mam kilka metod, które wszystkie mają bardzo podobny wzór. Biorą opcjonalny argument session
, który domyślnie przyjmuje wartość None
. Jeśli zostanie przekazany session
, funkcja używa tej sesji, w przeciwnym razie otwiera się i używa nowej sesji. Na przykład, rozważmy następujący sposób:Python udekoruj metody ze zmienną liczbą argumentów pozycyjnych i opcjonalnie arg
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
Tak, są nowe do Python i chętny do nauki całej swojej mocy, myślałem, że to pachniał jak doskonały czas, aby dowiedzieć się co nieco o dekoratorów Pythona. Więc po dużo czytania, jak to this series of blog posts i this fantastyczny SO odpowiedzieć, napisałem następujące dekorator:
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
I wydaje się działać świetnie. Oryginalna metoda jest teraz zredukowana do:
@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()]
Jednak, kiedy zastosować dekorator do funkcji, która wymaga pewnego pozycyjnych argumentów oprócz opcjonalnego session
, otrzymuję komunikat o błędzie. Więc próbuje napisać:
@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()
)
i działa jak print(client.manager.stock_exists('AAPL'))
daje AttributeError
z następującym traceback:
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]
Więc jestem zgadywania przez traceback, że jestem brudząc się kolejność argumentów, ale nie mogę wymyślić, jak prawidłowo je zamówić. Mam funkcje, które chcę udekorować, które mogą przyjmować 0-3 argumentów oprócz session
. Czy ktoś może wskazać błąd w mojej metodologii?
Przekaż 'sesję' jako nazwany argument -' func (stuff, session = session) '. Ponadto, dlaczego wywołujesz 'func' dwa razy? Na koniec wygląda na to, że powinien istnieć menedżer kontekstu dla 'db.session'. – jwilner
dzięki @jwilner! dwukrotne wywołanie 'func' było po prostu nieporozumieniem z mojej strony na temat składni. Zmieniłem wywołanie 'func' na' result = func() ', a następnie zwróć wynik. Tak, również o menedżerze kontekstu na 'db.session'. Próbowałem uciąć kilka kodów, żeby lepiej wyodrębnić moje pytanie. –