美文网首页工作生活
python闭包函数与装饰器函数

python闭包函数与装饰器函数

作者: Xyxtank | 来源:发表于2019-07-02 16:34 被阅读0次

一、闭包函数

什么是闭包:python是一种面向对象的编程语言,在python中一切皆对象,这样就使得变量所拥有的属性,函数也同样拥有。这样我们可以理解,在函数内创建一个函数的行为是完全合法的,而这种函数就叫内嵌函数。内嵌函数(可以理解为内部函数)可以在外部函数作用域内正常调用,在外部函数作用域外则会报错。如果内嵌函数(可以理解为内部函数)引用了外部函数定义的对象(可以是外层之外,但不是全局变量),那么此时的函数就叫闭包函数。

总之,一句话。如果外部函数的变量,被内部函数所引用,这种方式就是闭包。

比如:实现一个常规的累加函数,这是常规写法:

def func():
    num1 = 1
    num2 = 2
    return num1 + num2
print(func())
3

使用闭包函数的写法:

def func(num1):
    def add(num2):
        return num1 + num2
    return add
a = func(1)
print(a(2))
3

在这里,func()相当于外部函数,add()相当于内部函数,外部函数(func)的变量num1被内部函数(add)引用,这种方式就是闭包。另外,需要注意的是外部函数返回的是内部函数名。写法可以固定为:

def func1():
    def func2():
        ...
    return func2 #外部函数返回的是内部函数名

对比以上两种写法,其实看不出闭包有什么优势,反而变得更加繁琐。现在再举个例子,加深对闭包优势的了解。

比如,设计一个计数器:

def counter():
    cnt = [0]
    def add_one():
        cnt[0] += 1
        return cnt[0]
    return add_one

num1 = counter()
print(num1())
print(num1())
print(num1())
print(num1())
1
2
3
4

其实用常规方法设计的函数也可以实现,但换一种思路,在实际生活中,需求是不断变化的,现在需要一种从10开始的计数器,难道我们又要重新开发一套程序吗?没有更好的办法了吗?现在闭包的优势就来了。

def counter(FIRST=0):
    cnt = [FIRST]
    def add_one():
        cnt[0] += 1
        return cnt[0]
    return add_one

num1 = counter() #从1开始的计数器
num10 = counter(10)#从10开始的计数器
print(num1())
print(num1())
print(num1())
print(num10())
print(num10())
print(num10())
1
2
3
11
12
13

在这里,我们只需要简单的更改少量的代码counter(10),就可以实现新的需求。

在实际测试工作中,我们会经常测试程序的性能,其中一个主要的指标是看程序的运行时间,在此场景中,闭包函数就会经常被使用,它会大大提高工作的效率。比如:

import time

def cost_time(func):
    def wrapper():
        start_time = time.time()
        func()
        end_time = time.time()
        print(f'程序运行时间为:{end_time - start_time}')#f字符串格式化在pyhton3.7开始使用
    return wraper

@cost_time #装饰器
def my_func():
    time.sleep(2)

my_func()
程序运行时间为:2.0004215240478516

这里引用了装饰器,一般闭包函数和装饰器是配套使用的,这会大大提高python程序的效率。下面介绍装饰器的用法:

二、装饰器函数

什么是装饰器:python装饰器(function decorators)就是用来扩展函数功能的一种函数,其目的就是不改变原函数名(或类名)的情况下,给函数增加新的功能。装饰器就是通过闭包函数来给原来函数增加功能。因为调用函数不美观,所以引用了语法糖,也就是在需要添加功能的函数前面加上@即可。

需要注意,@修饰符的用法。

  • '@’符号用作函数修饰符,必须出现在函数定义的前一行。不允许和函数定义在同一行。什么意思?还是用上面的例子举例:
@cost_time #'@’符号用作函数修饰符,必须出现在函数定义的前一行,这就是第一行。
def my_func():#这是函数,应该在'@’函数修饰符的下一行。
    time.sleep(2)
  • '@’符号用作函数修饰符,必须模块或者类定义层内对函数进行修饰,不允许修饰一个类。
  • 一个修饰符就是一个函数,它将被修饰的函数作为参数,并返回修饰的同名函数或其他可调用的东西。
@cost_time #装饰器
def my_func():
    time.sleep(2)

这里的@,我们称之为语法糖,@cost_time就相当于cost_time(my_func),只不过更加的简洁。

  1. 带一个参数的装饰器

在之前计算程序运行时间的例子中,my_func()函数没有带参数,如果需要带参数呢?

import time

def cost_time(func):
    def wrapper(info):
        print("this is decorator")
        start_time = time.time()
        func(info)
        end_time = time.time()
        print(f'程序运行时间为:{end_time - start_time}')
    return wraper

@cost_time
def my_func(info):
    print(info)
    time.sleep(1)

my_func("hello world")
this is decorator
hello world
程序运行时间为:1.0005288124084473
  1. 带多个参数的装饰器

在这里, my_func(info)带了info参数,如果要实现的程序需要多个参数怎么办呢?一般情况下,我们会把*args和**kwargs,,作为装饰器内部函数wrapper()的参数。

def decorator(func):
    def wrapper(*args, **kwargs):
        func(*args, **kwargs)
    return wrapper
  1. 带自定义参数的装饰器

装饰器可以接受原函数任意类型和数量的参数,还可以接受自定义的参数,比如我要想控制程序运行的次数。

import time

def repeat(num):
    def cost_time(func):
        def wrapper(*args,**kwargs):
            start_time = time.time()
            for i in range(num):
                print("this is decorator")
                func(*args,**kwargs)
            end_time = time.time()
            print(f'程序运行时间为:{end_time - start_time}')
        return wrapper
    return cost_time

@repeat(3)
def my_func(info):
    print(info)
    time.sleep(1)

my_func("hello world")
print(my_func.__name__)
this is decorator
hello world
this is decorator
hello world
this is decorator
hello world
程序运行时间为:3.006178617477417
wrapper#不再是my_func函数

这里需要注意的是my_func(info)函数被装饰后,元信息发生了改变,print(my_func.name)显示的结果是wrapper函数,不再是原来的my_func函数。为了解决这个问题,可以使用内置的装饰器@functools.wrap,它会帮助保留原函数的元信息,其本质也就是将原函数的元信息,拷贝到对应的装饰器函数里。

import time
import functools

def cost_time(func):
    @functools.wraps(func)#使用内置的装饰器@functools.wrap,它会帮助保留原函数的元信息
    def wraper(info):
        print("this is decorator")
        start_time = time.time()
        func(info)
        end_time = time.time()
        print(f'程序运行时间为:{end_time - start_time}')
    return wraper

@cost_time
def my_func(info):
    print(info)
    time.sleep(1)

my_func("hello world")
print(my_func.__name__)
this is decorator
hello world
程序运行时间为:1.0009491443634033
my_func#元信息保留了
  1. 装饰器的嵌套

python也支持多个装饰器,比如:

@decorator1
@decorator2
def hello(info):
    print(info)

它等同于

decorator1(decorator2(hello))

执行的顺序是从左到右执行,比如:

import functools

def decorator1(func):
    @functools.wraps(func)
    def wrapper(*args,**kwargs):
        print("this is decorator1")
        func(*args,**kwargs)
    return wrapper

def decorator2(func):
    @functools.wraps(func)
    def wrapper(*args,**kwargs):
        print("this is decorator2")
        func(*args,**kwargs)
    return wrapper

@decorator1
@decorator2
def hello(info):
    print(info)

hello("hello world!")
this is decorator1
this is decorator2
hello world!

先执行的是decorator1,然后再执行decorator2,最后执行hello函数。

相关文章

  • python装饰器

    装饰器简述 要理解装饰器需要知道Python高阶函数和python闭包,Python高阶函数可以接受函数作为参数,...

  • python之理解闭包和装饰器

    python之理解闭包和装饰器 1、闭包函数 1.1 python中函数都是对象 结果: 上面定义一个shut函数...

  • 2020-012 python闭包与装饰器

    python闭包与装饰器 闭包 函数和对其周围状态(lexical environment,词法环境)的引用捆绑在...

  • python中的函数

    关于python中的值传递和址传递 匿名函数 缺省函数 闭包 装饰器

  • Python装饰器-专题笔记

    学会装饰器,Python更进阶 函数作用域到闭包到装饰器讲解,及闭包和装饰器的运用。 [√] 慕课网Meshare...

  • chapter7 函数式编程

    闭包 匿名函数 装饰器 偏函数

  • Python 装饰器的诞生过程

    Python中的装饰器是通过利用了函数特性的闭包实现的,所以在讲装饰器之前,我们需要先了解函数特性,以及闭包是怎么...

  • 只需四步,让你了解Python装饰器的诞生过程

    Python中的装饰器是通过利用了函数特性的闭包实现的,所以在讲装饰器之前,我们需要先了解函数特性,以及闭包是怎么...

  • Python 2 - 高级用法 - 装饰器

    Python 2 - 高级用法 - 装饰器 一谈到 装饰器,就离不开闭包 闭包 闭包就是能够读取其他函数内部变量的...

  • Python 中的闭包

    外部函数返回内部函数简称闭包。闭包是装饰器的基础,装饰器就是用于改变原来函数状态,方法的函数。因为函数可接受的参数...

网友评论

    本文标题:python闭包函数与装饰器函数

    本文链接:https://www.haomeiwen.com/subject/smkthctx.html