[python] 闭包以及装饰器

一 递归函数

  • 一个函数在内部调用自己那就是递归函数
  • 注意: 递归函数必须要有出口,否则将无限制递归下去
  • 每一次更深层的递归要比上一层规模小

1.1 阶乘应用

  • 使用普通函数和匿名函数的方法实现阶乘
# 阶乘函数
def factorial(n):
    if n == 0:
        return 1
    else:
        return n * factorial(n-1)
number = int(input("请输入你想要计算的阶乘:"))

result = factorial(number)
print(f"输入的{number}的阶乘是{result}")

# 使用lambda写这个阶乘
jiecheng = lambda n : 1 if n == 0 else n * jiecheng(n-1)
number = int(input("请输入你想要计算的阶乘:"))

result = jiecheng(number)
print(f"输入的{number}的阶乘是{result}")

1.2 斐波那契列数列

  • 这是一个经典的递归案例,1,1,2,3,5,8,13...
  • 从第三项开始,每一项都等于前两项之和。
def febonaqie(n):
    if n <= 1:
        return n
    return  febonaqie(n - 1) + febonaqie(n - 2)

number = int(input("请输入你想要计算的斐波那契数列:"))
result = febonaqie(number)
print(f"输入的{number}的数列和是{result}")

1.3 递归的优缺点

  • 优点:简介,清晰
  • 缺点:反复调用函数,运行效率低

二 闭包

  • 条件:
  1. 函数嵌套
  2. 内层函数使用外层函数的局部变量
  3. 外层函数的返回值是内层函数的函数名
  • 闭包变量: 外层函数中的局部变量,但被内部函数引用。

  • 闭包环境:包含闭包变量的环境,每个闭包都有其自己的闭包环境。
  •  是指一个嵌套函数(内部函数),它可以访问并记住其外层函数(非全局作用域)中定义的变量,即使外层函数已经执行完毕。

2.1 使用

使用闭包函数创建一个计数器函数

def make_addr():
    count = 0
    def adder(num):
        nonlocal count
        count += num
        return count
    return adder
# 创建一个adder函数实例
adder_instance = make_addr()
# 增加计数
print(adder_instance(1))
print(adder_instance(1))
print(adder_instance(1))

为什么adder_instance能够调用内函数呢?

  • 在python中,函数名里面保存了函数所在位置的引用
  • 比如a = 1,a只是一个变量名a 存放的是指向整数对象 1 的引用(可以理解为“地址”),而不是直接存储值本身。
  • 而adder_instance也就相当于是保存了函数地址的一个引用。

2.2 每次调用内函数使用同一份闭包变量

  • 每次调用外函数(如 make_addr())都会创建一个新的闭包环境
  • adder_instance调用了一次外部函数,此时创建了一个包含count闭包变量的函数调用空间。
  • 切记是调用如果这样调用他依然是不同的,因为这样你调用了两次,即使命名相同。

adder_instance = make_addr()
print(id(adder_instance))
adder_instance = make_addr()
print(id(adder_instance))

  • 这时候使用adder_instance,无论调用多少次使用的闭包变量都始终是同一份
  • 闭包变量是外函数局部变量的一部分,一旦外函数被调用,这些变量就被“捕获”并绑定到内函数上。即使外函数已经执行完,这些变量也不会被销毁,而是被内函数持续持有和使用。所以每次调用内函数,使用的都是那一份被捕获的变量。

三 装饰器

  • 本质: 是一个函数,可以让函数不修改原有代码的前提下增加额外的功能
  • 返回值: 也是一个函数对象
  • 条件
  • 不修改原代码
  • 不改变原有调用方法

3.1 标准版装饰器

def send():
    print("send: ")
def outer_func(fn):
    def in_func():
        # 在这里可以增加额外的功能
        print("recv:")
        fn()  # 满足闭包的第二个条件,使用外函数的局部变量
    return in_func

# 调用
outer_func(send)()

3.2 语法糖

  • 格式: @装饰器名称

  • 相当于程序读到了语法糖,执行前面定义的装饰器,然后把fn传递进去

  •  这样只要调用这个send_2的函数就会先执行装饰器

#装饰器
def outer_func2(fn):
    def in_func():
        fn()
        print("语法糖:")
    return in_func


#语法糖
@outer_func2
def send_2():
    print("发送消息:")
send_2()

3.3 被装饰的函数有参数

  • 内函数带参数  那么被装饰的函数也是要带参数的
# 装饰器
def out_func(fn):
    def in_func(name):              # 内函数带参数  那么被装饰的函数也是要带参数的
        print(f"内函数名称:{name}")
        fn(name)
    return in_func


#@out_func
def func(name):
    print("被装饰的函数")
func("语法糖")  # 语法糖调用
out_func(func)("语法糖") 标准装饰器调用方法

  • 要注意的是如果这样调用上面的语法糖
in1 = out_func(func("语法糖"))

# 会首先执行func("语法糖") r然后这个函数里面是print("被装饰的函数") 对于他返回值是None
# 然后就会得到out_func(None) 相当于外部函数绑定了一个None的内函数在其作用空间

3.4 被装饰的函数有可变参数

# 被装饰的函数有可变参数 *args, **kwargs
def args_func(*args,**kwargs):
    print(args)
    print(kwargs)
args_func(name='德玛西亚')


# 装饰器
def args_out_func(fn):
    def in_func(*args,**kwargs):
        fn(*args,**kwargs)
    return in_func

args_out_func(args_func)(area= '艾欧尼亚',name = "德莱尔斯")

3.5 多个装饰器

  • # 多个装饰器装饰过程,离函数最近的先装饰依次往外
  • # 首先执行装饰器2: -> deco2 被装饰的函数1
  • # 然后将(deco2 被装饰的函数1) 传递给装饰器1  ->deco1 deco2 被装饰的函数1
def deco1(fn):
    def inner():
        return "deco1 " + fn()
    return inner
def deco2(fn):
    def inner():
        return "deco2 " + fn()
    return inner
# 被装饰的函数
@deco1
@deco2
def deco1_ed():
    return "被装饰的函数1"

# 多个装饰器装饰过程,离函数最近的先装饰依次往外
# 首先执行装饰器2: -> deco2 被装饰的函数1
# 然后将(deco2 被装饰的函数1) 传递给装饰器1  ->deco1 deco2 被装饰的函数1

print(deco1_ed())

你可能感兴趣的:(python,python,java,前端)