2015-06-07 17 views
5

Tôi đang viết ứng dụng Python (3.4) đầu tiên của mình bằng SQLalchemy. Tôi có một số phương pháp mà tất cả đều có một mô hình rất giống nhau. Họ lấy một đối số tùy chọn session mặc định là None. Nếu session được chuyển, hàm sử dụng phiên đó, nếu không nó sẽ mở và sử dụng một phiên mới. Ví dụ, hãy xem xét các phương pháp sau đây:Phương pháp trang trí Python với số lượng biến số của các vị trí arg và tùy chọn 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 

Vì vậy, bị mới để Python và mong muốn tìm hiểu tất cả sức mạnh của nó, tôi nghĩ giống cá đối này như thời điểm hoàn hảo để tìm hiểu một chút gì đó về trang trí Python. Vì vậy, sau rất nhiều đọc, như thế này this series of blog poststhis tuyệt vời SO trả lời, tôi đã viết những trang trí sau:

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 

Và nó dường như làm việc tuyệt vời. Các phương pháp ban đầu bây giờ giảm xuống còn:

@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()] 

Tuy nhiên, khi tôi áp dụng trang trí để một hàm mang theo một số đối số vị trí, thêm vào các tùy chọn session, tôi nhận được một lỗi. Vì vậy, cố gắng viết:

@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() 
       ) 

và chạy như print(client.manager.stock_exists('AAPL')) đưa ra một AttributeError với traceback sau:

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] 

Vì vậy, tôi đoán bởi traceback, mà tôi đang rối tung lên thứ tự của các đối số, nhưng tôi không thể tìm ra cách đặt hàng đúng cách. Tôi có các chức năng mà tôi muốn trang trí có thể lấy 0-3 đối số ngoài các session. Ai đó có thể vui lòng chỉ ra lỗi trong phương pháp của tôi?

+0

Truyền 'phiên' làm đối số được đặt tên -' func (stuff, session = session) '. Ngoài ra, tại sao bạn gọi 'func' hai lần? Cuối cùng, điều này thực sự có vẻ như cần có một trình quản lý ngữ cảnh cho 'db.session'. – jwilner

+0

cảm ơn @jwilner! gọi 'func' hai lần chỉ là một sự hiểu lầm về phần của tôi trên cú pháp. Tôi đã thay đổi lệnh 'func' thành' result = func() 'và sau đó trả về kết quả. Vâng, cũng đúng về một trình quản lý ngữ cảnh trên 'db.session'. Tôi đã cố gắng cắt một loạt các mã ra để cô lập câu hỏi của tôi tốt hơn. –

Trả lời

3

Thay đổi

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) 

Nó hoạt động:

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') 

Output:

$ python3 test.py 
<object object at 0x7f4197810070> 
ticker <object object at 0x7f4197810070> 

Khi bạn sử dụng def inner(that, session=None, *args, **kwargs) bất kỳ đối số vị trí thứ hai (tính self) được coi là 01.đối số. Vì vậy, khi bạn gọi manager.stock_exists('AAPL')session nhận giá trị AAPL.

+0

Cảm ơn, Điều này thực sự hữu ích. Tôi có cảm giác tôi vừa trộn thứ tự của args. –

1

Điều đầu tiên tôi nhận thấy là bạn đang gọi chức năng trang trí hai lần

@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 

Trong phiên gọi thứ hai sẽ được đã đóng cửa. Ngoài ra, bạn không cần phải chấp nhận rõ ràng các thông số thatsession trong chức năng trang trí, chúng đã có trong argskwargs.Hãy nhìn vào giải pháp này:

@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 

Bạn cũng có thể muốn đặt mã phiên bế mạc trong finally khối, sau đó Bạn sẽ chắc chắn rằng nó được đóng lại ngay cả khi chức năng trang trí ném một ngoại lệ

+0

mà không rõ ràng truyền 'that' trong' inner() 'và' func() ', tôi nhận được' NameError: name 'mà' không được định nghĩa' –

Các vấn đề liên quan