python函数装饰器

装饰器

一、无参装饰器

1.什么是装饰器

器是指工具,而程序中的函数就是具备某一功能的工具,所以装饰器指的是为装饰器对象额外对象添加额外功能,因此定义装饰器就是定义一个函数,只不过该函数的功能是用来为其他函数添加额外功能的。

需要注意的是:

  • 装饰器本身其实是可以任意调用的对象
  • 被装饰的对象也可以是任意可调用的对象

2.为什么要用装饰器

如果我们已经上线 了一个项目,需要修改某一个方法,但是我们不想修改方法的使用方法,这个时候可以使用装饰器。因为软件的维护应该遵循开发封闭原则,即软件于丹上线运行后,软件的维护对修改原码是封闭的,对扩展功能指的是开发的。

装饰器的实现必须遵守俩大规则:

  1. 不修改被装饰对象的源代码。
  2. 不修改被装饰对象的调用方式。

装饰器其实就是在遵守以上俩个原则的前提下被装饰对象添加新功能。

怎么使用装饰器

改变源代码

import time


def index():
    start = time.time()
    print('welcome to index')
    time.sleep(1)
    end = time.time()
    print('运行时间:', end - start)


index()
#输出:
welcome to index
运行时间: 1.0003464221954346

编写重复代码

import time


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


def f2():
    print('welcome to f2')
    time.sleep(1)


start = time.time()
index()
end = time.time()
print('运行时间:', end - start)

start = time.time()
f2()
end = time.time()
print('运行时间:', end - start)


#输出:
welcome to index
运行时间: 1.0001671314239502
welcome to f2
运行时间: 1.0003397464752197

第一种传参方式:改变调用方式

import time


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


def time_count(func):
    start = time.time()
    func()
    end = time.time()
    print('运行时间:', end - start)


time_count(index)

#输出:
welcome to index
运行时间: 1.0005521774291992

第二种传参方式:包给函数-外包

import time


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


def time_count(func):
    def wrapper():
        start = time.time()
        func()
        end = time.time()
        print('运行时间:', end - start)

    return wrapper


index = time_count(index)
index()
 #输出:
   welcome to index
运行时间: 1.0000648498535156

4.完善装饰器

上述的装饰器,最后调用index()的时候,其实是在调用wrapper(),因此如果原始的index()有返回值的时候,wrapper()函数的返回值应该和index()的返回值相同,也就是说,我们需要同步原始的index()和wrapper()方法的返回值。

import time


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


def time_count(func):
    def wrapper():
        start = time.time()
        res = func()
        end = time.time()
        print('运行时间:', end - start)

        return res

    return wrapper


index = time_count(index)
res = index()
print(res)
#输出:
welcome to index
运行时间: 1.0007765293121338
123

如果原始的index()方法需要传参,那么我们之前的装饰器是无法实现该功能的,由于有wrapper()=index(),所以给wrapper()方法传参即可。

import time


def home(name):
    print(f'welcome {name} to home page')
    time.sleep(1)
    return name


def time_count(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        res = func(*args, **kwargs)
        end = time.time()
        print(f'{func} time is {end - start}')
        return res

    return wrapper


home = time_count(home)
res = home('chen')
print('res:', res)

#输出:
welcome chen to home page
<function home at 0x000002085D50B0D8> time is 1.0006473064422607
res: chen

5.装饰器 语法糖

在被装饰函数正上方,并且是单独一行写上@装饰器名

import time


def time_count(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        res = func(*args, **kwargs)
        end = time.time()
        print(f'{func} time is {end - start}')
        return res

    return wrapper


@time_count
def home(name):
    print(f'welcome {name} to home page')
    time.sleep(1)
    return name


@time_count
def index():
    print('welcome to index')
    time.sleep(1)
    return 123


res = home('egon')
print(f"res:{res}")

#输出:
welcome egon to home page
<function home at 0x00000203FABAC948> time is 1.0005333423614502
res:egon

6.装饰器模板

def func():
    def wrapper(*args,**kwargs):
        res = func(*args,**kwargs)
        return res
    return wrapper

二、有参装饰器

无参装饰器只套了两层,有参装饰器是套三层。

#登陆注册的装饰器
import time

current_user = {'username': None}


def login(func):
    def wrapper(*args, **kwargs):
        if current_user['username']:
            res = func(*args, **kwargs)
            return res
        user = input('username:').strip()
        pwd = input('password:').strip()
        if user == 'chen' and pwd == '123':
            print('login successful')
            current_user['username'] = user
            res = func(*args, **kwargs)
            return res
        else:
            print('user or password error')

    return wrapper


@login
def home(name):
    print(f'welcome {name} to home page')
    time.sleep(1)
    return name


@login
def index():
    print('welcome to index')
    time.sleep(1)
    return 123


res = index()
#输出:
username:chen
password:123
login successful
welcome to index

对于上面的登陆注册,我们把用户登陆成功的信息写入内存文档中,但是在工业在,内存信息可以存在文本中,mysql中,mongodb中,但是我们只能让用户信息来自于file的用户可以认证,因此我们可以改写上述的装饰器。

import time

current_user = {'username': None}


def login(func):
    def wrapper(*args, **kwargs):
        if current_user['username']:
            res = func(*args, **kwargs)
            return res
        user = input('username:').strip()
        pwd = input('password:').strip()
        engine = 'file'
        if engine == 'file':
            print('base of file')
            if user == 'chen' and pwd == '123':
                print('login successful')
                current_user['username'] = user
                res = func(*args, **kwargs)
                return res
            else:
                print('user or password error')
        elif engine == 'mysql':
            print('base of mysql')
        elif engine == 'mongodb':
            print('base of mongodb')
        else:
            print('default')

    return wrapper


@login
def home(name):
    print(f'welcome {name} to home page')
    time.sleep(1)


@login
def index():
    print('welcome to index')
    time.sleep(1)


res = index()

#输出:
username:chen
password:123
base of file
login successful
welcome to index

1.三层闭包

三层简单实例

def f1(y):
    def f2():
        x = 1

        def f3():
            print('x:', x)
            print('y:', y)

        return f3

    return f2


f2 = f1(2)
f3 = f2()
f3()
#输出:
x: 1
y: 2

现在需要修改原来需求,需要判断用户动态的获取用户密码的方式,如果是file类型的我们则让用户进行认证,因此我们需要使用,有参装饰器。

import time

current_user = {'username': None}


def auth(engine='file'):
    def login(func):
        def wrapper(*args, **kwargs):
            if current_user['username']:
                res = func(*args, **kwargs)
                return res
            user = input('username:').strip()
            pwd = input('password:').strip()
            if engine == 'file':
                print('base of file')
                if user == 'chen' and pwd == '123':
                    print('登陆成功')
                    current_user['username'] = user
                    res = func(*args, **kwargs)
                    return res
                else:
                    print('用户密码或者用户名错误')
            elif engine == 'mysql':
                print('base of mysql')
            elif engine == 'mongodb':
                print('base of mongodb')
            else:
                print('please base of file')

        return wrapper

    return login


@auth(engine='mysql')
def home(name):
    print(f'welcome {name} to home page')
    time.sleep(1)


@auth(engine='file')
def index():
    print('welcome to index')
    time.sleep(1)


res = index()
#输出:
username:chen
password:123
base of file
登陆成功
welcome to index

由于倆层的装饰器,参数必须得固定定位func,但是三层的装饰器解除了这个限制,我们不仅仅可以使用上述单个参数的三层装饰器,多个参数的只需要在三层装饰器中多家几个参数即可,也就是说装饰器三层即可,多加无用。

俩个装饰器的叠加

def outter1(func):  # func = wrapper2
    def wrapper1(*args, **kwargs):  # wrapper是未来要运行的函数
        print('------------')
        res = func(*args, **kwargs)  # func是被装饰的函数  # wrapper2
        print('------------')
        return res

    return wrapper1


def outter2(func):  # func = index
    def wrapper2(*args, **kwargs):  # wrapper是未来要运行的函数
        print('11111111111111')
        res = func(*args, **kwargs)  # func是被装饰的函数  # index()
        print('11111111111111')
        return res

    return wrapper2


# @outter1  # index = outter1(index)
# @outter2  # index = outter2(index)  # 先运行最下面的装饰器
# # index
def index():
    print('index')


# index重新定义的index = outter2(index 真正的index)
index = outter2(index)  # index = wrapper2
# index再一次重新定义的index = outter1(index重新定义的index,即wrapper2)
index = outter1(index)  # index  = wrapper1
index()  # wrapper1()

#输出:
------------
11111111111111
index
11111111111111
------------

重要,装饰器的模板

双层装饰器

def outter(func):
    def wrapper(*args,**kwargs):
        #加功能
        res = func(*args,**kwargs)
        return res
    return wrapper

三层装饰器模板

主要是用来给 倆层装饰器添加参数

def sanceng(engine):
    def outter(func):
        def wrapper(*args, **kwargs):
            # 加功能
            print(engine)
            res = func(*args, **kwargs)
            return res

        return wrapper

    return outter


@sanceng('file')
def shopping():
    print('shopping')


shopping()
#输出:file
shopping
posted @ 2019-08-12 19:43  豆瓣酱瓣豆  阅读(201)  评论(0编辑  收藏  举报