python裝飾器(符號@)示例

1. 應用需求

購物車場景,有兩個函數get_nameget_info,已實現

def get_name(sess):
    name = sess['name']
    return name


def get_info(sess):
    info = sess['info']
    return info

假設現在接到一個需求,需驗證登錄後才能獲取nameinfo。如果我們在已有源碼上直接修改,即添加if語句,這會違反開閉原則(對擴展開放,對修改關閉)。這裏介紹Python開發模式中一款強大的工具——裝飾器(24種開發模式就是強大)。

2. 裝飾器

2.1. 不使用修飾符的方法

直接用代碼實現,有

def get_name(sess):
    name = sess['name']
    return name


def get_info(sess):
    info = sess['info']
    return info


def check_for_app_1(func, sess):
    is_login = sess['is_login']
    if is_login:
        return func(sess)
    else:
        return None


def check_for_app_2(func, sess):
    level = sess['level']
    if level > 5:
        return func(sess)
    else:
        return None


if __name__ == '__main__':
	# 接到需求1:需驗證是否登錄
    sess = {'name': 'app_1.name', 'info': 'app_1.info', 'is_login': True}
    name = check_for_app_1(get_name, sess)
    print(name)
    sess = {'name': 'app_1.name', 'info': 'app_1.info', 'is_login': False}
    name = check_for_app_1(get_name, sess)
    print(name)

	# 中途改需求,接到需求2:轉爲需驗證等級
    sess = {'name': 'app_2.name', 'info': 'app_2.info', 'level': 2}
    name = check_for_app_2(get_name, sess)
    print(name)
    sess = {'name': 'app_2.name', 'info': 'app_2.info', 'level': 10}
    name = check_for_app_2(get_name, sess)
    print(name)

運行結果:
app_1.name
None
None
app_2.name

2.2 使用修飾符符號@的方法

使用更加簡便的寫法,有

from functools import wraps
# 推薦百度@wraps學習該語句的功能
# 推薦百度*args、**kwargs學習該語句的功能


def check_for_app_1(func):
    @wraps(func)
    def _check(*args, **kwargs):
        # print(args)
        # print(type(args))
        # print(type(args[0]))
        sess = args[0]
        if sess['is_login']:
            return func(*args, **kwargs)
        else:
            return None

    return _check


# 如果需改需求,可添加多一個check_for_app_2方法
# 同時將@check_for_app_1修改成@check_for_app_2
@check_for_app_1
def get_name(sess):
    name = sess['name']
    return name


@check_for_app_1
def get_info(sess):
    info = sess['info']
    return info


if __name__ == '__main__':
    sess = {'name': 'app_1.name', 'info': 'app_1.info', 'is_login': True}
    print(get_name(sess))
    print(get_info(sess))

    sess = {'name': 'app_1.name', 'info': 'app_1.info', 'is_login': False}
    print(get_name(sess))
    print(get_info(sess))

運行結果:
app_1.name
app_1.info
None
None

get_xxx函數不帶參數,有

def check_for_app_1(func):
    def _check():
        print('write to log.txt', func.__name__)  # 記錄到日誌裏
        return func()

    return _check


@check_for_app_1
def get_name():
    return 'name'


@check_for_app_1
def get_info():
    return 'info'


if __name__ == '__main__':
    print(get_name())
    print(get_info())

運行結果:
write to log.txt get_name
name
write to log.txt get_info
info

2.3 符號@的有趣用法

符號@的裝飾器可以帶參數,有

def check_for_app_1(log_path):
    def _check_for_app_1(func):
        def _check(sess):
            print(log_path, end='\t')
            if sess['is_login']:
                return func(sess)
            else:
                return None

        return _check

    return _check_for_app_1


def check_for_app_2(func):
    def _check(sess):
        if sess['level'] >= 3:
            return func(sess)
        else:
            return None

    return _check


# 調用get_name方法時記錄到log_name.txt日誌中去
@check_for_app_1('D:/log_name.txt')
def get_name(sess):
    name = sess['name']
    return name


# 調用get_info方法時記錄到log_app.txt日誌中去
# 接到新需求,調用get_info方法時需同時驗證登錄和等級
@check_for_app_1('D:/log_app.txt')
@check_for_app_2
def get_info(sess):
    info = sess['info']
    return info


if __name__ == '__main__':
    print()

    sess = {'name': 'app_1.name', 'info': 'app_1.info', 'is_login': True, 'level': 5}
    print(get_name(sess))
    print(get_info(sess))
    print()

    sess = {'name': 'app_1.name', 'info': 'app_1.info', 'is_login': False, 'level': 0}
    print(get_name(sess))
    print(get_info(sess))
    print()

    sess = {'name': 'app_2.name', 'info': 'app_2.info', 'is_login': True, 'level': 0}
    print(get_name(sess))
    print(get_info(sess))
    print()

運行結果:

D:/log_name.txt app_1.name
D:/log_app.txt app_1.info

D:/log_name.txt None
D:/log_app.txt None

D:/log_name.txt app_2.name
D:/log_app.txt None

其實上述用法等價於

def check_for_app_1(log_path):
    def _check_for_app_1(func):
        def _check(sess):
            print(log_path, end='\t')
            if sess['is_login']:
                return func(sess)
            else:
                return None

        return _check

    return _check_for_app_1


def check_for_app_2(func):
    def _check(sess):
        if sess['level'] >= 3:
            return func(sess)
        else:
            return None

    return _check


def get_name(sess):
    name = sess['name']
    return name


def get_info(sess):
    info = sess['info']
    return info


if __name__ == '__main__':
    sess = {'name': 'app_1.name', 'info': 'app_1.info', 'is_login': True, 'level': 2}
    # sess = {'name': 'app_1.name', 'info': 'app_1.info', 'is_login': True, 'level': 21}

    get_name_1 = check_for_app_1('E:/log_name.txt')(get_name)
    print(get_name_1(sess))

    get_info_1 = check_for_app_2(get_info)
    get_info_1 = check_for_app_1('E:/log_info.txt')(get_info_1)
    print(get_info_1(sess))

運行結果:
E:/log_name.txt app_1.name
E:/log_info.txt None

3. Reference

python函數修飾符@的使用
手把手帶你學會Python

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