程序员文章、书籍推荐和程序员创业信息与资源分享平台

网站首页 > 技术文章 正文

二十七、Python嵌套函数-装饰器模式实现原理

hfteth 2025-07-03 15:21:58 技术文章 2 ℃


什么是嵌套函数

Python中嵌套函数,也称作内部函数,是在函数内部定义的函数。需要注意的是:

  • 外部函数的变量可以被内部函数所使用,但不能被内部函数修改
  • 若一定要修改,需要添加nonlocal关键字,使用了nonlocal关键字,将变量标记为不在本地作用域定义,而在上一级局部作用域中定义,但不能是全局作用域中定义。nonlocal只能用在嵌套函数的内部

嵌套函数的作用

嵌套函数主要用于:

  • 数据隐藏:外部无法访问嵌套函数及嵌套函数内的变量;
  • 利用代码:外部函数内有多个地方逻辑处理相同,因此在其内部定义函数可以减少重复代码;
  • 闭包原则: 在很多编程语言中都存在闭包的概念,那什么是闭包呢?闭包其实就是一个概念,出现在嵌套函数中,指的是:内层函数引用到了外层函数的自由变量,就形成了闭包。

自由变量:未在本地作用域中定义的变量,比如在嵌套函数的外层定义的变量(非全局变量),对内层来说,这个变量就叫做自由变量。

嵌套函数使用

# 函数的闭包: 在函数内部对嵌套函数进行返回操作称之为闭包
def outer():
    def inner():
        print('我是内部inner在打印')
    return inner # 注:这里是返回inner的引用,不能加(),加了相当于调用inner

# 那怎样才能正确调用outer呢?
# 通过下面这行代码调用outer(), 返回的inner的引用, 所以这不是正确的调用方式
print(outer()) # <function outer.<locals>.inner at 0x7fb5702eccb0>

# 下面这行代码才是正确地调用outer的方式
# 首先outer()返回了inner()的地址引用, 然后再调用
outer()() # 我是内部inner在打印

# 上面的代码相当于
f_innter = outer()
f_innter() # 我是内部inner在打印

嵌套函数用于函数装饰器(三层嵌套)

函数装饰器,是指在不改变某一函数(outer)的情况下,增强另一个函数的功能(inner),这种在代码运行期间动态增加功能的方式,称之为“装饰器”。

下面通过代码示例来逐步模拟“装饰器”的实现过程:

  • 1.定义嵌套函数,外层函数接收一个外部函数的引用,在内部函数中调用外部传入的函数
def cartel(f):
    def doperunner():
        print('贩毒小团伙内部开始计划...')
        f() #调用外部传入内部函数的逻辑,f的功能由传入的函数定义
        print('贩毒小团伙内部开始行动...')
    return doperunner

# 定义要传入的函数
def spy():
    print(f'我要把间谍注入贩毒集团下的分部')
    print(f'间谍开始收集情报')
    print(f'间谍回馈情报')
    print(f'总部收到间谍情报,得知小团伙近期行动计划,开始布署')

# 在本例中,cartel相当于一个闭包的函数,比较特殊的是它提供了一个接口f。
# 这个参数是函数,因此,要调用cartel函数,要通过以下方法:
f = cartel(spy)
f()

2.Python语法糖@改进调用方法

def cartel(f):
    def doperunner():
        print('贩毒小团伙内部开始计划...')
        f() #调用外部传入内部函数的逻辑,f的功能由传入的函数定义
        print('贩毒小团伙内部开始行动...')
    return doperunner

# 语法糖
@cartel
# 定义要传入的函数
def spy():
    print(f'我要把间谍注入贩毒集团下的分部')
    print(f'间谍开始收集情报')
    print(f'间谍回馈情报')
    print(f'总部收到间谍情报,得知小团伙近期行动计划,开始布署')


#========>等同于 f = cartel(spy) f()
spy()

函数带参数的装饰器

def cartel(f):
    def doperunner(*args, **kwargs):
        print('贩毒小团伙内部开始计划...')
        f(*args,**kwargs) #调用外部传入内部函数的逻辑,f的功能由传入的函数定义
        print('贩毒小团伙内部开始行动...')
    return doperunner

# 语法糖
@cartel
def spy(*args, **kwargs):
    for name in args:
        print(f'把间谍{name}注入贩毒集团下的分部')
        print(f'间谍{name}开始收集情报')
        print(f'间谍{name}回馈情报')
        print(f'总部收到间谍{name}情报,得知小团伙近期行动计划,开始布署')
    if kwargs:
        print(kwargs)

# 调用
spy(['梁朝伟','刘嘉玲'])

装饰器带参数

def is_exposed(flag='否'):
    def cartel(f):
        def doperunner():
            print('贩毒小团伙内部开始计划...')
            if flag == '是':
                print('间谍已暴露')
            f() #调用外部传入内部函数的逻辑,f的功能由传入的函数定义
            print('贩毒小团伙内部开始行动...')
        return doperunner
    return cartel

# 定义要传入的函数
@is_exposed()
def spy():
    print(f'我要把间谍注入贩毒集团下的分部')
    print(f'间谍开始收集情报')
    print(f'间谍回馈情报')
    print(f'总部收到间谍情报,得知小团伙近期行动计划,开始布署')
# 第一次调用
spy()

print('我是分隔线'.center(30,'#'))

@is_exposed('是')
def spy():
    print(f'我要把间谍注入贩毒集团下的分部')
    print(f'间谍开始收集情报')
    print(f'间谍回馈情报')
    print(f'总部收到间谍情报,得知小团伙近期行动计划,开始布署')
# 第二次调用
spy()

Tags:

最近发表
标签列表