Python-闭包与装饰器

闭包函数

闭包函数本身是一个函数,是把闭包函数内部的函数 + 函数内部的变量包裹起来,然后通过返回值 返回出去。

闭包函数必须得符合函数嵌套

# 闭包函数:本身是一个函数,让其调用更方便
# 定义
# 把一个函数名 + 变量 封装成一个函数  返回(return)出去

def out_fun(url):
    # url = "qinyj"
    def iner_fun():
        print(url)
    return iner_fun

a = out_fun("url")
a()

现在有一个函数,老板有一个需求,想在这个函数上加一个计时功能,就是计算这个函数执行时间,需求吩咐下去,某某公司的3个运维开发开始思考了。。。

def index():
    print(from index)

第一个人 小青开始做。。。他的方式是修改源代码:

import time
def index():
    start = time.time()
    print("from index")
    time.sleep(1)
    end = time.time()
    print(end - start)

index()
from index
1.0080575942993164

第二个人 小杰开始做。。。他的方式是在调用的时候计算时间

import time
def index():
    print("from index")
    time.sleep(1)

start = time.time()
index()
end = time.time()
print(end - start)
from index
1.0000572204589844

第三个人 小强开始做。。。他用了一个非常牛逼的看不懂的骚操作。。

import time

def index():
    print("from index")

def time_count(func):
    def inner():
        start = time.time()
        func()
        time.sleep(1)
        end = time.time()
        print(end - start)
    return inner

index = time_count(index)
index()
from index
1.0000572204589844

好了,现在三个人已经全部做完,老板该验收了,把他们三个人叫到会议室。。。

指明了小强做的非常不错!另外两个向他学习,小强做的方式就是装饰器

什么叫装饰器?

装饰器:本身是一个函数,给已有的函数加功能

原则:

不改变源代码

不改变其调用方式

刚才只有小强的代码符合上面规定

  1. 装饰器本身是函数,只不过它用来装饰 被装饰的函数
  2. 装饰器装饰函数 不改变被装饰函数源代码
  3. 装饰器装饰函数 不改变被装饰函数的调用方式

实现装饰器

# v1   实现最简单的装饰器

import time

def index():
    """被装饰的函数"""
    print('index')
    time.sleep(1)

# time_count装饰器:对被装饰函数计时
def time_count(func):  # func才是真正的index
    """装饰器"""

    def wrapper():
        start = time.time()
        func()
        end = time.time()
        print(end - start)

    return wrapper

index = time_count(index)  # index == wrapper
index()  # wrapper()

# v2:带返回值

import time

def index():
    """被装饰的函数"""
    print('x',x)
    print('index')
    time.sleep(1)
    return 'index'

# time_count装饰器:对被装饰函数计时
def time_count(func):  # func才是真正的index
    """装饰器"""

    def wrapper():
        start = time.time()
        res = func()  # index()
        end = time.time()
        print(end - start)

        return res

    return wrapper

index = time_count(index)  # index == wrapper
res = index()  # wrapper()
print(res)

# v3:加参数

import time


def index(x,y,z=10):
    """被装饰的函数"""
    print('x',x)
    print('index')
    time.sleep(1)

    return 'index'


# time_count装饰器:对被装饰函数计时
def time_count(func):  # func才是真正的index
    """装饰器"""

    def wrapper(*args,**kwargs):  # (10, 20)  # *args和**kwargs接收了所有的参数
        start = time.time()
        res = func(*args,**kwargs)  # index()  # *(10,20)  # *args和**kwargs打散参数传给真正的index
        end = time.time()
        print(end - start)

        return res

    return wrapper


index = time_count(index)  # index == wrapper
res = index(10,20,320)  # wrapper()
print(res)

以上均为二层装饰器:

  1. 用来装饰函数的,它本质是函数

  2. 不改变函数源代码

  3. 不改变函数调用方式

如果实在理解不了,就记住装饰器模板:


from functools import wraps

def deco(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        # 要加什么功能就加上去
        res = func(*args, **kwargs)

        return res

    return wrapper

这里提到了一个wraps,其实Python装饰器(decorator)在实现的时候,被装饰后的函数其实已经是另外一个函数了(函数名等函数属性会发生改变),为了不影响,Python的functools包中提供了一个叫wraps的decorator来消除这样的副作用。写一个decorator的时候,最好在实现之前加上functools的wrap,它能保留原有函数的名称和docstring。
废话不多说,上俩例子就能搞明白!
实例一:
不加wraps

# -*- coding=utf-8 -*- 
from functools import wraps   
def my_decorator(func):
    def wrapper(*args, **kwargs):
        '''decorator'''
        print('Calling decorated function...')
        return func(*args, **kwargs)
    return wrapper  
 
@my_decorator 
def example():
    """Docstring""" 
    print('Called example function')
print(example.__name__, example.__doc__)

# 执行结果:
# ('wrapper', 'decorator')

实例二:
加wraps

# -*- coding=utf-8 -*- 
from functools import wraps   
def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        '''decorator'''
        print('Calling decorated function...')
        return func(*args, **kwargs)
    return wrapper  
 
@my_decorator 
def example():
    """Docstring""" 
    print('Called example function')
print(example.__name__, example.__doc__)

# 执行结果:
# ('example', 'Docstring')

warps 作用: 消除或修复(被装饰后的函数名等属性的改变)副作用

装饰器语法糖

在被装饰的函数上 加上 @装饰器函数名,目的是为了让代码更加简洁。

@time_count
def index(x,y,z=10):
    """被装饰的函数"""
    print('x',x)
    print('index')
    time.sleep(1)

    return 'index'

三层装饰器

三层装饰器就是给装饰器加参数,在原有的两层装饰器上再嵌套一层函数,利用闭包的思想,把函数变量和函数名返回出去。

username_list = []


def sanceng(role):
    def login_deco(func):
        def wrapper(*args, **kwargs):

            if username_list:
                print('已经登录,请勿重复登录')
                res = func(*args, **kwargs)
                return res

            username_inp = input('请输入用户名:')
            pwd_inp = input('请输入密码:')

            with open(f'{role}_info.txt', 'r', encoding='utf8') as fr:
                for user_info in fr:
                    username, pwd = user_info.strip().split(':')
                    if username_inp == username and pwd_inp == pwd:
                        print('登录成功')
                        username_list.append(username)

                        res = func(*args, **kwargs)
                        return res

                else:
                    print('登录失败')

        return wrapper

    return login_deco

@sanceng('admin')
def index(x, y):
    print('index')
    print('x,y', x, y)

    return 123

res = index(10, 20)
print(res)
posted @ 2019-09-23 17:11  GeminiMp  阅读(158)  评论(0编辑  收藏  举报