装饰器 - Python

装饰器说明

器指的工具(只要是工具,你就应该想到函数),装饰指的是为被装饰对象添加新功能,需要注意的是:项目一旦上线之后,就应该遵循开发封闭的原则。开发封闭指的是对修改函数内的源代码和调用方式是封闭的,对功能的扩展是开放的。看起来有点矛盾,但这就是我们要做的。在这样的要求下,我们必须要找到一种解决方案,能够在不修改一个功能内源代码以及调用方式的前提下,为其添加新功能。这就用到了装饰器,它能够在不修改被装饰对象源代码与调用方式的前提下,为被装饰器对象添加新功能。

无参装饰器

(1) 无参装饰器实现过程

无参装饰器指的是装饰器本身没有参数。

# 要求:为index函数添加一个统计时间的功能
import time  # 这是一个与时间相关的模块


def index():
    time.sleep(3)  # 睡3秒
    print('welcome to index page')


index()

# 版本一(只有index函数可以使用)
import time  # 这是一个与时间相关的模块


def index():
    time.sleep(3)  # 睡3秒
    print('welcome to index page')


start_time = time.time()  # 从1970年开始计时的时间戳
index()
end_time = time.time()
print('run time is %s' % (end_time - start_time))


# 版本二(两个函数都可以使用,但是有大量重复代码)
def index():
    time.sleep(3)
    print('welcome to index page')


def home(name):
    time.sleep(5)
    print('welcome %s to home page' % name)


start_time = time.time()
index()
stop_time = time.time()
print('run time is %s' % (stop_time - start_time))

start_time = time.time()
home('Albert')
stop_time = time.time()
print('run time is %s' % (stop_time - start_time))

# 版本三(修改了源函数的调用方式)
import time


def index():
    time.sleep(3)
    print('welcome to index page')


def home(name):
    time.sleep(5)
    print('welcome %s to home page' % name)


def wrapper(func):  # func=index
    start_time = time.time()
    func()  # index()
    stop_time = time.time()
    print('run time is %s' % (stop_time - start_time))


wrapper(index)

# 版本四(使用闭包函数,不修改源函数调用方式)
import time


def index():
    time.sleep(3)
    print('welcome to index page')


def outer(func):  # func=最原始的index
    # func=最原始的index
    def wrapper():
        start_time = time.time()
        func()
        stop_time = time.time()
        print(stop_time - start_time)

    return wrapper


# a = outer(index)  # outer函数结果可以赋值给任意变量
# b = outer(index)
# c = outer(index)

index = outer(index)  # 赋值给index覆盖原来的index,index = wrapper

index()  # wrapper()

# 版本五(解决原函数返回值无效)
import time


def index():
    time.sleep(1)
    print('welcome to index page')
    return 1  # 假如源函数有一个返回值


def outer(func):
    # func=最原始的home
    def wrapper():
        start_time = time.time()
        res = func()  # 调用最原始的index
        stop_time = time.time()
        print(stop_time - start_time)
        return res

    return wrapper


index = outer(index)  # 新的index=wrapper
res = index()  # 上一个版本返回值为None
print(res)

# 版本六(终极版,解决有参函数和无参函数通用的问题)

import time


def index():
    time.sleep(1)
    print('welcome to index page')
    return 1


def home(name):
    time.sleep(2)
    print('welcome %s to home page' % name)


def timer(func):  # 装饰器也是一个函数,我们给他一个好听的名字
    def wrapper(*args, **kwargs):  # wrapper函数有无参数由源函数决定
        start_time = time.time()
        res = func(*args, **kwargs)
        stop_time = time.time()
        print(stop_time - start_time)
        return res

    return wrapper


index = timer(index)  # 新的index=wrapper
home = timer(home)  # 新的home=wrapper

home(name='Albert')  # wrapper(name='Albert')
home('Albert')  # wrapper('Albert')
index()  # wrapper()


# 无参装饰器模板
def outer(func):
    def inner(*args, **kwargs):
        """
        这里写装饰器逻辑
        :param args: 任意位置参数
        :param kwargs: 任意关键参数
        :return: 一个函数对象
        """
        res = func(*args, **kwargs)
        return res

    return inner

(2) 装饰器语法糖

import time


# 装饰器也是一个函数,使用函数必先定义,所以装饰器放在最上方
def timer(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)
        stop_time = time.time()
        print(stop_time - start_time)
        return res

    return wrapper


@timer  # 在被装饰对象正上方单独一行添加,相当于执行index=timer(index)
def index():
    time.sleep(1)
    print('welcome to index page')
    return 1


# @timer  # home=timer(home) 当不需要装饰器的时候只需注释这一行即可
def home(name):
    time.sleep(2)
    print('welcome %s to home page' % name)


index()
home('Albert')

(3) 用户认证装饰器

import time

current_user = {
    'username': None,
}


def auth(func):
    def wrapper(*args, **kwargs):
        if current_user['username']:
            print('已经登陆过了')
            res = func(*args, **kwargs)
            return res

        name = input('用户名>>: ').strip()
        pwd = input('密码>>: ').strip()
        if name == 'Albert' and pwd == '1':
            print('登陆成功')
            current_user['username'] = name
            res = func(*args, **kwargs)
            return res
        else:
            print('用户名或密码错误')

    return wrapper


@auth
def index():
    time.sleep(1)
    print('welcome to index page')
    return 1


@auth
def home(name):
    time.sleep(2)
    print('welcome %s to home page' % name)


index()
home('Albert')

(4) 多个装饰器叠加

import time

current_user = {
    'username': None,
}


def auth(func):
    def wrapper(*args, **kwargs):
        if current_user['username']:
            print('已经登陆过了')
            res = func(*args, **kwargs)
            return res

        name = input('用户名>>: ').strip()
        pwd = input('密码>>: ').strip()
        if name == 'Albert' and pwd == '1':
            print('登陆成功')
            current_user['username'] = name
            res = func(*args, **kwargs)
            return res
        else:
            print('用户名或密码错误')

    return wrapper


def timer(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)
        stop_time = time.time()
        print(stop_time - start_time)
        return res

    return wrapper


"""
@auth  
@timer  # 这样写的话timer只统计index的执行时间
"""


@timer  # timer 统计的是auth+index的执行时间
@auth
def index():
    time.sleep(1)
    print('welcome to index page')
    return 1


index()

有参装饰器

参装饰器就是装饰器本身需要一个参数,结合我们以前讲过的文件操作,其实文件就是存放数据的仓库,类似于数据库,数据库分很多种,常见的有MySQL,Oracle,PostgreSQL和DB2等等,在一些项目的需求中,不同的数据会分散存储在不同的数据库中,这时我们使用基于对象的数据模型(通俗讲就是使用编程语言来操作数据库)操作不同数据库就要执行不同的代码。

import time

current_user = {
    'username': None,
}


def auth(engine):
    def user_auth(func):
        def wrapper(*args, **kwargs):
            if engine == 'file':
                print('基于文件的认证')
                if current_user['username']:
                    print('已经登陆过了')
                    res = func(*args, **kwargs)
                    return res

                name = input('用户名>>: ').strip()
                pwd = input('密码>>: ').strip()
                if name == 'Albert' and pwd == '1':
                    print('登陆成功')
                    current_user['username'] = name
                    res = func(*args, **kwargs)
                    return res
                else:
                    print('用户名或密码错误')
            elif engine == 'mysql':
                print('基于MyQL的认证')
            elif engine == 'ldap':
                print('基于LDAP的认证')
            elif engine == 'postgresql':
                print('基于PostgreSQL的认证')

        return wrapper

    return user_auth


@auth('file')  # auth装饰器本身是一个函数,在语法糖中也可以传参数
def index():
    time.sleep(1)
    print('welcome to index page')
    return 1


index()

装饰器补充

from functools import wraps


def deco(func):
    @wraps(func)  # 加在最内层函数正上方
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)

    return wrapper


@deco
def index():
    '''哈哈哈哈'''
    print(index.__doc__)


index()

装饰器使用的是闭包函数的原理,返回的是和原来函数同名字的函数地址,再加上()就能调用这个函数,所以给我们的感觉是原来的函数没有变化,却添加了新的功能,其实已经不是原来的函数了,你可以把以上代码的第三行注释掉,运行代码,打印结果为None,就是因为你运行的函数已经不是原来的函数了,所以这其实是一个伪装饰器,要想让装饰器真的是装饰器,调用别人写好的包,返回结果还是原函数,以上写法就是。

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章