python函数高级

Python函数进阶全套学习笔记(函数对象/嵌套/名称空间/闭包/装饰器)

一、函数第一类对象(函数对象)

1.1 核心概念

  函数是第一类对象:函数可以像普通变量一样被使用。

  Python中所有名字本质都是内存地址绑定关系:

  • 变量名:绑定内存地址,调用时解析为变量值

  • 函数名:直接绑定函数内存地址,不加括号是引用地址,加括号是执行函数体

1.2 函数对象的四大特性

  1、函数名可以被赋值

# 变量赋值
x = 10
y = x

# 函数赋值
def f1():
    print("from f1....")

f2 = f1  # f2与f1指向同一个函数内存地址
print(f2)
f2()     # 执行函数

  2、函数名可以作为函数参数

def f1():
    print('from f1...')

# 接收函数对象作为参数
def f3(m):
    print(m)  # 打印函数内存地址
    m()       # 执行传入的函数

f3(f1)  # 传入函数名,不要加括号

  3、函数名可以作为函数返回值

def f1():
    print('from f1')

def f3(m):
    return m  # 返回函数对象

res = f3(f1)
print(res)
res()  # 执行返回的函数

  4、函数名可以作为容器类型元素

x = 33
def f1():
    print("from f1")

# 列表存储函数对象
l = [11,22,x,f1]
l[3]()  # 调用列表中的函数

1.3 实战案例:功能映射字典

  利用函数对象特性,实现输入编号调用对应功能,替代大量if判断

# 定义功能函数
def login():
    print('from login...')

def withdrawal():
    print("from withdrawal....")

def transfer():
    print("from transfer....")

def recharge():
    print("from recharge...")

def xxx():
    print("xxx")

# 字典映射:编号 = [函数对象, 功能描述]
func_dic = {
    "1": [login,"登录"],
    "2": [withdrawal,"提现"],
    "3": [transfer,'转账'],
    "4": [recharge,"充值"],
    "5": [xxx,"xxx功能"],
}

# 循环交互
while True:
    # 打印所有功能
    for k in func_dic:
        print(f"{k} {func_dic[k][1]}")
    # 接收用户输入
    choice = input("请输入命令编号:").strip()
    if choice not in func_dic:
        print("必须输入正确的命令编号!")
        continue
    # 调用对应功能函数
    func_dic[choice][0]()

二、函数嵌套(定义嵌套 + 调用嵌套)

2.1 函数嵌套分类

  • 函数嵌套定义:在一个函数内部定义另一个函数,默认仅内部可调用

  • 函数嵌套调用:调用函数的过程中,再次调用其他函数

2.2 嵌套调用案例

# 求两个数最大值
def max2(x,y):
    if x > y:
        return x
    else:
        return y

# 嵌套调用,求四个数最大值
def max4(a,b,c,d):
    res1 = max2(a,b)
    res2 = max2(res1,c)
    res3 = max2(res2,d)
    print(res3)

max4(10,100,33,11)

2.3 嵌套定义案例

  内层函数默认仅外层函数内部可用,可通过return将内层函数暴露到外部

def f1():
    # 嵌套定义内层函数
    def f2():
        print("f2..............")
    f2()  # 内部调用
    return f2  # 返回内层函数对象

# 外部获取并调用内层函数
res = f1()
res()

2.4 实战案例:圆形计算工具

from math import pi

def circle(radius,mode):
    # 嵌套定义:计算周长
    def perimeter(radius):
        return 2 * pi * radius
    # 嵌套定义:计算面积
    def area(radius):
        return pi * (radius * radius)
    # 根据模式调用不同内层函数
    if mode == 0:
        return perimeter(radius)  # 0-周长
    elif mode == 1:
        return area(radius)       # 1-面积

print(circle(10,0))  # 周长
print(circle(10,1))  # 面积

三、名称空间与作用域

3.1 名称空间核心概念

  名称空间:专门存放「名字与内存地址绑定关系」的区域,用来管理变量、函数名

3.2 三大名称空间

  1. 内置名称空间:存放Python内置名字(len、print、input等),解释器启动即创建

  2. 全局名称空间:存放.py文件顶级代码定义的名字(函数、全局变量),文件运行时创建

  3. 局部名称空间:存放函数内部定义的名字,函数调用时创建、调用结束销毁

3.3 名称空间加载顺序

  启动Python程序:内置名称空间 → 全局名称空间 → 局部名称空间(函数调用时)

3.4 名字查找优先级(LEGB规则)

  核心规则:查找优先级在函数定义阶段就固定,与调用位置无关

  优先级从高到低:L(局部) → E(外层嵌套) → G(全局) → B(内置)

  经典案例验证

# 案例1:层级查找
x = 222
def f1():
    x = 111
    def f2():
        print(x)  # 优先找外层f1的x=111
    f2()
f1()

# 案例2:定义阶段固定查找规则
len = 12345
def f1():
    def f2():
        print(len)  # 定义时外层无len,调用全局len=12345
    f2()
f1()

# 案例3:局部先声明后使用(报错案例)
def f1():
    print(len)  # 定义阶段检测到局部会定义len,优先局部,未赋值直接调用报错
    len = 111
# f1()  # 运行报错

3.5 作用域分类

  1. 全局作用域:内置、全局名称空间,全局存活、全局有效

  2. 局部作用域:局部名称空间,临时存活、仅函数内部有效

四、global与nonlocal关键字

4.1 global关键字

  作用:在局部作用域中修改全局变量

# 基础用法
x = 100
def f1():
    global x  # 声明使用全局x
    x = 200
f1()
print(x)  # 输出200

# 可变类型无需global(修改内部数据,不修改绑定地址)
l = [111,222]
def f2():
    l.append(333)  # 无需声明global
f2()
print(l)  # [111,222,333]

# 重新赋值可变类型需要global
def f3():
    global l
    l = 333
f3()
print(l)  # 333

4.2 nonlocal关键字

  作用:在内层局部修改外层函数的局部变量,逐层向外查找,顶层无则报错(不查找全局)

def f1():
    x = 111
    def f2():
        nonlocal x  # 声明修改外层函数的x
        x = 222
    f2()
    print(x)  # 输出222

f1()

五、闭包函数

5.1 闭包函数定义

  同时满足两个条件即为闭包函数:

  1. 函数嵌套定义(内层函数)

  2. 内层函数引用了外层函数的局部变量

5.2 闭包核心作用

  解决:函数无法直接传参时,通过外层函数包裹参数,为内层函数传参,不改变函数作用域

  传参两种方式对比

  方式一:普通参数传参(简单场景)

def wrapper(x):
    print("from wrapper---->", x)

wrapper(111)
wrapper(222)

  方式二:闭包传参(函数不支持传参时使用)

def f1(x):
    # 闭包函数,引用外层参数x
    def wrapper():
        print("from wrapper---->", x)
    return wrapper

# 提前绑定参数,生成专属函数
wrapper1 = f1(111)
wrapper2 = f1(222)
wrapper1()
wrapper2()

  重点:装饰器的底层原理就是闭包函数

六、装饰器核心原理与实战

6.1 装饰器定义

  装饰器是基于闭包实现的工具,用于在不修改原函数源代码、不改变原函数调用方式的前提下,为函数拓展新功能

6.2 设计原则:开放封闭原则

  • 封闭:不修改已有功能的源代码

  • 开放:支持拓展新功能

6.3 装饰器迭代演进

  版本1:修改源代码(不推荐,违反原则)

import time
def index():
    start = time.time()
    time.sleep(3)
    print("from index...")
    stop = time.time()
    print("run time is %s" % (stop - start))
    return 200
index()

  版本2:调用处加代码(冗余严重)

import time
def index():
    time.sleep(3)
    print("from index...")
    return 200

# 每次调用都要重复写统计代码
start = time.time()
index()
stop = time.time()
print("run time is %s" % (stop - start))

  版本3:固定函数装饰(不通用)

import time
def index():
    time.sleep(3)
    print("from index...")
    return 200

# 只能装饰index,无法复用
def wrapper():
    start = time.time()
    index()
    stop = time.time()
    print("run time is %s" % (stop - start))
wrapper()

  版本4:通用无参装饰器(改变调用方式)

import time
def index():
    time.sleep(3)
    print("from index...")
    return 200

# 通用装饰函数
def wrapper(func):
    start = time.time()
    func()
    stop = time.time()
    print("run time is %s" % (stop - start))

wrapper(index)  # 调用方式改变,不满足需求

  版本5:完整版通用装饰器(闭包实现、不改变调用方式)

import time
def timer(func):
    # 兼容任意参数、保留原函数返回值
    def wrapper(*args,**kwargs):
        # 新增功能:统计耗时
        start = time.time()
        # 执行原函数
        res = func(*args,**kwargs)
        # 新增功能结束
        stop = time.time()
        print("run time is %s" % (stop - start))
        # 保留原函数返回值
        return res
    return wrapper

# 手动替换:不改变调用方式
def index():
    time.sleep(3)
    print("from index...")
    return 200

index = timer(index)
index()

  6.4 语法糖 @(简化装饰器调用)

  @装饰器名 等价于 函数名 = 装饰器名(函数名)

import time
def timer(func):
    def wrapper(*args,**kwargs):
        start = time.time()
        res = func(*args,**kwargs)
        stop = time.time()
        print("运行时间:%s" % (stop - start))
        return res
    return wrapper

# 语法糖简化
@timer
def home(x,y):
    time.sleep(2)
    print("This is home ....",x, y)
    return 200

@timer
def index(y):
    time.sleep(3)
    print("This is index ....",y)
    return 200

index('iii')

  6.5 修复装饰器函数属性(wraps)

  问题:装饰后的函数会丢失原函数的__name__、__doc__属性

  解决:使用functools.wraps保留原函数属性

from functools import wraps

def deco(func):
    @wraps(func)  # 保留原函数属性
    def wrapper(*args, **kwargs):
        print("装饰器代码运行啦。。。")
        res = func(*args, **kwargs)
        return res
    return wrapper

@deco
def index():
    """这是xxx功能"""
    print("from index")
    return 123

help(index)  # 正常显示原函数文档
print(index.__name__)  # 输出index,而非wrapper

  6.6 无参装饰器通用模板

from functools import wraps

def 装饰器名(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        # 前置新增功能
        res = func(*args, **kwargs)  # 执行原函数
        # 后置新增功能
        return res  # 返回原函数返回值
    return wrapper
posted @ 2026-07-10 11:55  cuckooyang  阅读(18)  评论(0)    收藏  举报