Python的裝飾器小記

Python裝飾器

1、簡介
本質: Python的裝飾器就是一個閉包。
目的: 簡化代碼操作

2、使用裝飾器的原則:不改變被裝飾函數的屬性等性質

  • 使用中間人g對象幫助傳遞參數
  • 使用內層裝飾器@functools.wraps(view_func)回覆被裝飾函數的屬性等性質(舉例2)

3、舉例1:定義驗證登錄狀態的裝飾器

# 使用中間人g對象作爲裝飾器和被裝飾函數中的參數傳遞者
from flask import session, jsonify, g
from myihome.utils.response_code import RET
import functools  # python的內置模塊,存放函數工具

# 閉包:外層函數一般就是定義爲被裝飾的函數(view_func(例如這裏是:set_user_avatar))"的@外層函數"
def login_required(view_func):
    # 內層函數一般定義爲wrapper,並且由於傳遞的參數不確定,使用*args, **kwargs待定
    # @functools.wraps(view_func)這個函數裝飾器專門是用來裝飾內層函數的,
    # 1參數:外層函數接受的參數,直接傳給裏面的就可以了,
    # 2意義:內層裝飾器加上之後會改變一些特性:functools的wraps會將wrapper相關的屬性和名字恢復爲view_func的屬性和名字,參考博客的例子2
    @functools.wraps(view_func)  # 在寫裝飾器的時候需要習慣將這個內層裝飾器補上,避免改變被裝飾函數的特性
    def wrapper(*args, **kwargs):
        # 判斷用戶的登錄狀態
        user_id = session.get("user_id")

        # 如果用戶是登錄的,執行視圖函數
        if user_id is not None:
            # g對象的應用,保存user_id,讓其作爲參數傳遞對象,在視圖函數中可以通過g對象獲取保存數據
            g.user_id = user_id
            return view_func(*args, **kwargs)
        else:
            # 如果未登錄,返回未登錄的信息
            return jsonify(errno=RET.SESSIONERR, errmsg="用戶未登錄")
    return wrapper


# 使用中間人g對象作爲裝飾器和被裝飾函數中的參數傳遞者,g對象就是提供來保存數據的
# 在一次請求之中如果涉及到多個函數請求參數的時候就可以使用g對象來傳參數
@login_required
def set_user_avatar():
	# 本來是可以直接操作session獲取user_id的,
	# 但是使用的裝飾器裏面已經獲取到了user_id,由裝飾器的原則,不可能變成 def set_user_avatar(user_id):的,所以可以使用中間人g對象傳遞過來,不必重複操作一遍
	# user_id = session.get("user_id")
    user_id = g.user_id
    pass

# set_user_avatar() 的執行就是執行wrapper-> wrapper() 直接傳遞參數到wrapper()裏面

4、舉例2: 內層裝飾器@functools.wraps(func)的作用
①首先:未加上裝飾器:

def login_required(func):
    def wrapper(*args, **kwargs):
        pass
    return wrapper

def test():
    """test python"""
    pass

print(test.__name__) 
print(test.__doc__)  

Python中萬物皆對象,直接打印出函數test()的名字和說明文檔:

test
test python

②加上裝飾器:

def login_required(func):
    def wrapper(*args, **kwargs):
        pass
    return wrapper

@login_required
def test():
    """test python"""
    pass
# test -> wrapper :執行test(), 實質是執行wrapper()
print(test.__name__)  # wrapper.__name__
print(test.__doc__)  # wrapper.__doc__

打印的結果爲:

wrapper
None

可見已經改變test()的屬性了,違反了裝飾器的原則。
③加上內層函數裝飾器:

import functools

def login_required(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        pass
    return wrapper
    
@login_required
def test():
    """test python"""
    pass
    
# test -> wrapper

print(test.__name__)  # wrapper.__name__
print(test.__doc__)  # wrapper.__doc__

打印結果爲:

test
test python

可見被裝飾函數的屬性被恢復了。

參考代碼及項目URL:
https://github.com/too-hoo/myiHome/blob/master/myihome/utils/commons.py

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章