【Python查漏补缺(一)】闭包和装饰器

发布时间 2023-06-08 22:48:41作者: FlowersandBoys

闭包的构成条件:

  1. 在函数嵌套(函数里面再定义函数)的前提下

  2. 内部函数使用了外部函数的变量(还包括外部函数的参数)

  3. 外部函数返回了内部函数

简单闭包

def fun_out(num1):
   # 定义外部函数
   def fun_inner(num2):
       # 内部函数使用了外部函数的变量
       result = num2 + num1
       print("结果是: ", result)
   # 外部函数返回了内部函数,这里返回的内部函数就是闭包
   return fun_inner
 
f = fun_out(1)  # 创建闭包实例

f(2)
f(3)
  1. 当返回的内部函数使用了外部函数的变量就形成了闭包

  2. 闭包可以对外部函数的变量进行保存

闭包的作用

*闭包的作用: 闭包不仅可以保存外部函数的变量还可以提高代码的可重用性(不需要再手动定义额外的功能函数*

def config_name(name):  # 定义外部函数
   # 内部函数使用了外部函数的变量
   def say_info(info):
       print(name + ":" + info)

   return say_info

tom = config_name("Tom")
tom("你好!")
tom("你好,在吗?")

jerry = config_name("jerry")
jerry("不在,不和你玩!")

 

装饰器的功能特点:

  1. 不修改已有函数的源代码

  2. 不修改已有函数的调用方式

  3. 给已有函数增加额外的功能

def check(fn):
   # 追加一个登陆验证的功能
   def inner():
       print("请先登陆!")
       fn()
   return inner


def comment():
   print("发表评论!")


comment = check(comment)
comment()
  • 闭包函数有且只有一个参数,必须是函数类型,这样定义的函数才是装饰器。

  • 写代码要遵循开放封闭原则,它规定已经实现的功能代码不允许被修改,但可以被扩展。

装饰器的语法糖写法

如果有多个函数都需要添加登录验证的功能,每次都需要编写func = check(func)这样代码对已有函数进行装饰,这种做法还是比较麻烦。

Python给提供了一个装饰函数更加简单的写法,那就是语法糖,语法糖的书写格式是: @装饰器名字,通过语法糖的方式也可以完成对已有函数的装饰

糖写法演示

@check
def comment():
   print("发表评论")

@check ==> comment = check(comment)

使用装饰器添加登陆验证功能

def check(fn):
   print("装饰函数执行了")
   def inner():
       print("请登陆....")
       fn()
   return inner

@check    # @check 等价于 comment = check(comment)
def comment():  # 装饰器的执行时间是加载模块时立即执行。
   print("发表评论!")

comment()  # 调用函数

 

使用装饰器对函数执行时间进行统计

import time

def get_time(func):
   # 定义函数的嵌套,
   def inner():
       begin_time = time.time()
       func()   # 内部函数使用了外部函数的变量包括参数
       end_tiem = time.time()
       print("函数执行花费了:%f" % (end_tiem-begin_time))
       print("测试:",end_tiem-begin_time)
   return inner  # 外部函数返回了内部函数


@get_time   # 等价于test = get_tiem(test)
def test():
   for i in range(10000000):
       i += 1
   print(i)

test()

带有不定长参数的装饰器

*args和**kwargs都是可变长度参数的方式

*args:用于传递不定数量的位置参数,它会将所有的位置参数打包成一个元组(tuple)。

**kwargs:用于传递不定数量的关键字参数,它会将所有的关键字参数打包成一个字典(dictionary)。

def logging(fun):  # 这里fun = sum_num
   # *args和**kwargs都是可变长度参数的方式,它们之间的区别如下:
   #
   # *args:用于传递不定数量的位置参数,它会将所有的位置参数打包成一个元组(tuple)。
   #
   # **kwargs:用于传递不定数量的关键字参数,它会将所有的关键字参数打包成一个字典(dictionary)。
   def inner(*args, **kwargs):
       print("--正在努力计算--")
       fun(*args, **kwargs)  # 调用sum_num
   return inner

@logging  # 这里sum_num = logging(sum_num)
# 执行到这里就有 sum_num = inner
def sum_num(*args, **kwargs):
   result = 0
   for vaules in args:  # args元组形式
       result += vaules

   for vaules in kwargs.values():  # kwargs将所有参数转化为字典类型的数据
       result += vaules

   print(result)

sum_num(1, 2, a=10)

通用装饰器

# 通用装饰器
def logging(fn):  # 2,将函数sum_num 作为参数传递
   def inner(*args, **kwargs):
       print("--正在努力计算--")
       result = fn(*args, **kwargs) # fn = sum_num, fn = subtraction
       return result

   return inner


# 使用语法糖装饰函数
@logging  # 1 这里sum_num = logging(sum_num) -- 之后sum_num = inner,调用sum_num相当于调用inner
def sum_num(*args, **kwargs):
   result = 0
   for value in args:
       result += value

   for value in kwargs.values():
       result += value

   return result


@logging  # 3,这里subtraction = logging(subtraction) 之后subtraction = inner,调用subtraction相当于调用inner
def subtraction(a, b):
   result = a - b
   print(result)

result = sum_num(1, 2, a=10)  # 调用sum_num 函数即调用inner函数,其中fun()为sum_num()本体
print(result)

subtraction(4, 2)