十三、嵌套函数、闭包函数以及装饰器

本文详细介绍了Python中的函数概念,包括嵌套函数的使用,如何通过闭包封装变量,以及装饰器的实现与应用。重点讲解了作用域规则、nonlocal关键字以及装饰器的实现原理和实际效果。通过示例展示了装饰器如何在不修改原函数代码的情况下扩展功能,同时提到了functools模块在装饰器中的作用,强调了规范装饰器编写的重要性。

嵌套函数、闭包函数、装饰器

一.嵌套函数

Python中以函数为作用域,在作用域中定义的相关数据只能被当前作用域或子作用域使用。

NAME = "kevin"
print(NAME)

def func():
    print(NAME)

func()

1.1 函数在作用域中

函数也是定义在作用域中的数据,在执行函数时候,也同样遵循:优先在自己作用域中寻找,没有则向上一接作用域寻找,例如:

# 1. 在全局作用域定义了函数func
def func():
    print("你好")
    
# 2. 在全局作用域找到func函数并执行。
func()


# 3.在全局作用域定义了execute函数
def execute():
    print("开始")
    # 优先在当前函数作用域找func函数,没有则向上级作用域中寻找。
    func()
    print("结束")

# 4.在全局作用域执行execute函数
execute()

作用域中的值在被调用时到底是啥?

  • 情景1

    def func():
        print("你好")
        
    func()
    
    def execute():
        print("开始")
        func()
        print("结束")
        
    execute()
    
    def func():
        print(666)
        
    func()
    
    # 输出
    你好
    开始
    你好
    结束
    666
    
  • 情景2

    def func():
        print("你好")
        
    func()
    
    def execute():
        print("开始")
        func()
        print("结束")
    
    def func():
        print(666)
    
    func()
    execute()
    
    # 你好
    # 666
    # 开始
    # 666
    # 结束
    

1.2 函数定义的位置

上述示例中的函数均定义在全局作用域,其实函数也可以定义在局部作用域,这样函数被局部作用域和其子作用于中调用(函数的嵌套)。

def func():
    print("上海名媛")
    
def handler():
    print("北京富婆")
    def inner():
        print("广州靓仔")
	inner()
    func()
    print("深圳靓女")

handler()

# 北京富婆
# 上海名媛
# 深圳靓女

到现在你会发现,只要理解数据定义时所存在的作用域,并根据从上到下代码执行过程进行分析,再怎么嵌套都可以搞定。

为什么要这么嵌套定义?把函数都定义在全局不好吗?

在一个函数去实现某功能,想要将内部功能拆分成N个函数,又担心这个N个函数放在全局会与其他函数名冲突时(尤其多人协同开发)可以选择使用函数的嵌套。

1.3 嵌套引发的作用域问题

基于内存和执行过程分析作用域。

name = "Kevin"

def run():
    name = "mia"
    def inner():
        print(name)
	inner()
    
run()

三句话搞定作用域:

  • 优先在自己的作用域找,自己没有就去上级作用域。
  • 在作用域中寻找值时,要确保此次此刻值是什么。
  • 分析函数的执行,并确定函数作用域链。(函数嵌套)

1.4关键字nonlocal

nonlocal应用于一个嵌套的函数的作用域中的一个名称, 而不是def之外的全局作用域。

def outer():
    num = 10
    def inner():
        nonlocal num   # nonlocal关键字声明
        num = 100
        print(num)
    inner()
    print(num)
outer()

# 输出
# 100
# 100

二、闭包函数

  • 闭 :内部的函数

  • 包 :包含了对外部函数作用域中变量的引用

闭包,简而言之就是将数据封装在一个包(区域)中,使用时再去里面取。(本质上 闭包是基于函数嵌套搞出来一个中特殊嵌套)

# 定义
def func(age):
    name = "Kevin"

    def f1():
        print(name, age)

    def f2():
        print(name, age)

    def f3():
        print(name, age)

    f1()
    f2()
    f3()

func(123)

三、装饰器

装饰器的本质就是一个闭包函数。

装饰器的功能:在不修改原函数及其调用方式的情况下对原函数功能进行扩展

# 定义一个装饰器

def outer(origin):
    def inner():
        print("before 110")
        res = origin()  # 调用原来的func函数
        print("after")
        return res

    return inner

# 使用装饰器
@outer
def func1():
    print("我是func1函数")
    value = (11, 22, 33, 44)
    return value


@outer
def func2():
    print("我是func2函数")
    value = (11, 22, 33, 44)
    return value


@outer
def func3():
    print("我是func3函数")
    value = (11, 22, 33, 44)
    return value


func1()
func2()
func3()
  • 实现原理:基于@语法和函数闭包,将原函数封装在闭包中,然后将函数赋值为一个新的函数(内层函数),执行函数时再在内层函数中执行闭包中的原函数。

  • 实现效果:可以在不改变原函数内部代码 和 调用方式的前提下,实现在函数执行和执行扩展功能。

  • 适用场景:多个函数系统统一在 执行前后自定义一些功能。

  • 装饰器示例

    def outer(origin):
        def inner(*args, **kwargs):
    		# 执行前
            res = origin(*args, **kwargs)  # 调用原来的func函数
            # 执行后
            return res
        return inner
    
    
    @outer
    def func():
        pass
    
    func()
    

重要补充:functools

你会发现装饰器实际上就是将原函数更改为其他的函数,然后再此函数中再去调用原函数。

def handler():
    pass

handler()
print(handler.__name__) # handler
def auth(func):
    def inner(*args, **kwargs):
        return func(*args, **kwargs)
    return inner

@auth
def handler():
    pass

handler()
print(handler.__name__) # inner
import functools

def auth(func):
    @functools.wraps(func)
    def inner(*args, **kwargs):
        return func(*args, **kwargs)
    return inner

@auth
def handler():
    pass

handler()
print(handler.__name__)  # handler

其实,一般情况下大家不用functools也可以实现装饰器的基本功能,但后期在项目开发时,不加functools会出错(内部会读取__name__,且__name__重名的话就报错),所以在此大家就要规范起来自己的写法。

import functools


def auth(func):
    @functools.wraps(func)
    def inner(*args, **kwargs):
        """眼巴巴"""
        res = func(*args, **kwargs)  # 执行原函数
        return res

    return inner
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值