Python 函数 - Functions in Python函数的定义与调用定义函数使用def关键字来定义函数语法为deffunction_name(parameters):# 函数体returnvalue# 可选返回值函数调用函数通过函数名和参数来调用resultfunction_name(arguments)返回值函数使用return关键字返回值。一个函数只能返回一个值但 Python 可以通过元组来模拟多个返回值。defadd(x,y):returnxydefget_values():return1,2,3# 实际上是返回了一个元组 (1, 2, 3)# 通过解构赋值接收多个返回值a,b,cget_values()yieldyield用于生成器函数它返回一个迭代器而不是一次性返回所有结果。调用生成器函数时并不会立即执行函数内部的代码而是返回一个生成器对象。**生成器会在yield处暂停执行并记住当前的状态如变量值。下次迭代时它会从yield停止的地方继续执行而不是从头开始。**这意味着生成器不会一次性返回所有结果而是按需生成。这使得生成器特别适合处理大量数据或者需要延时计算的场景。defcount_up_to(limit):count1whilecountlimit:yieldcount count1fornumberincount_up_to(5):print(number)为什么使用yield而不是return?延迟计算yield允许按需生成结果而不是一次性返回所有的值。对于大规模数据处理生成器非常高效因为它只在需要时生成下一个值避免了内存占用。状态保持生成器保存了函数的状态。每次yield都会保存当前的执行状态变量值等使得下次从停止的位置继续执行而不是从头开始。惰性求值生成器是“惰性求值”的也就是说只有在请求下一个值时它才会计算并返回这使得它能够在内存有限的情况下处理大量数据。生成器也可配合next()内建函数使用defcount_up_to(limit):count1whilecountlimit:yieldcount count1# 创建生成器对象countercount_up_to(5)# 使用 next() 显式获取下一个生成值print(next(counter))# 输出 1print(next(counter))# 输出 2print(next(counter))# 输出 3print(next(counter))# 输出 4print(next(counter))# 输出 5参数系统参数传递对象引用对象引用传递Python 中的参数传递机制是 “对象引用传递”意味着函数接受的是对象的引用而不是副本。对于可变对象如列表、字典在函数内部对其修改会影响原对象。对于不可变对象如整数、字符串、元组函数内的修改不会影响原对象。defmodify_list(lst):lst.append(1)my_list[1,2,3,4]modify_list(my_list)print(my_list)# 输出 [1, 2, 3, 4, 1]位置参数Positional Arguments位置参数按位置顺序传递给函数。defgreet(name,age):print(fHello{name}, you are{age}years old.)greet(Alice,30)关键字参数Keyword Arguments关键字参数通过keyvalue方式显式传递避免位置错误。defgreet(name,age):print(fHello{name}, you are{age}years old.)greet(nameAlice,age30)默认参数Default Parameters默认值如果未传递某个参数则使用默认值。defgreet(name,age30):print(fHello{name}, you are{age}years old.)greet(Alice)# age 使用默认值 30可变默认参数陷阱避免使用可变对象作为默认参数因为它们在函数调用中会被共享。defappend_to_list(value,lst[]):lst.append(value)returnlst# 会发生意外的共享效果result1append_to_list(1)result2append_to_list(2)print(result1)# 输出 [1, 2]print(result2)# 输出 [1, 2]应避免此类问题解决方法使用None作为默认值并在函数内创建新的对象defappend_to_list(value,lstNone):iflstisNone:lst[]lst.append(value)returnlst*args可变位置参数可变位置参数使用*args来接收任意数量的位置参数返回一个元组。defsum_numbers(*args):print(type(args))# 输出 class tuplereturnsum(args)print(sum_numbers(1,2,3))# 输出 6**kwargs可变关键字参数可变关键字参数使用**kwargs来接收任意数量的关键字参数返回一个字典。defprint_details(**kwargs):forkey,valueinkwargs.items():print(f{key}:{value})print_details(nameAlice,age30)parameters 通常指定义函数时声明的形式参数形参。arguments 通常指函数调用传入的实际参数实参。仅位置参数/和仅关键字参数*仅位置参数/通过/来指定某些参数只能通过位置传递不能通过关键字传递。deffoo(a,b,/,c,d):# / 前面的是仅位置参数后面的可通过关键字传递print(a,b,c,d)foo(1,2,3,4)# 正确foo(a1,b2,c3,d4)# 错误a 和 b 不能作为关键字参数foo(1,2,c3,d4)# 正确a、b 仅位置参数c、d可作为关键字参数仅关键字参数*通过*来指定某些参数只能通过关键字传递也就是说这些参数不能通过位置传递只能显式地指定其名称。deffoo(a,b,*,c,d):# * 后面的是仅关键字参数前面的可通过关键字传递print(a,b,c,d)foo(1,2,c3,d4)# a、b 通过位置传递c、d 通过关键字传递foo(1,2,3,4)# 错误c,d 只能通过关键字传递foo(a1,b2,c3,d4)# 正确a,b 也可以通过关键字参数传递混合使用deffoo(a,b,/,c,d,*,e,f):print(a,b,c,d,e,f)在这个函数中a和b是仅位置参数只能通过位置传递。c和d是普通的位置参数可以通过位置或关键字传递。e和f是仅关键字参数只能通过关键字传递。高级函数作为对象函数是第一类对象函数可以作为参数传递、返回值、赋值给变量等。defgreet(name):returnfHello,{name}!say_hellogreetprint(say_hello(Alice))高阶函数高阶函数Higher-Order Function是指接受一个或多个函数作为参数或者返回一个函数的函数。换句话说高阶函数的特点是它能够操作函数使得函数的处理更加灵活和抽象。defgreet(name):returnfHello,{name}!defprocess_greeting(func,name):# 接受一个函数和一个名字作为参数并执行该函数returnfunc(name)# 传递 greet 函数作为参数resultprocess_greeting(greet,Alice)print(result)# 输出 Hello, Alice!defmultiply_by(n):defmultiplier(x):returnx*nreturnmultiplier# 创建一个函数该函数将数字乘以 2doublemultiply_by(2)# 调用返回的函数print(double(5))# 输出 10defouter(strParam):definner(func):print(Before function be called.)func(strParam)print(After function be called.)returninnerdefgreet(name):print(fHello,{name})outer(Alice)(greet)# 高阶函数的调用两次函数调用的链式组合闭包 Closure闭包是指一个内嵌函数即函数内部定义的函数引用了其外部函数的变量形成了对这些变量的闭合引用。换句话说闭包允许内嵌继续访问外部函数的局部变量即使外部函数已经执行完毕并返回。用途延迟计算闭包允许在外部函数执行时保存某些信息并在稍后的时间里通过内嵌函数访问和操作这些信息。状态封装它可以封装一些状态信息避免这些状态暴露给外部达到更好的数据封装和保护。闭包由两个部分组成外部函数定义外部环境变量通常是用于某些计算或状态。内部函数使用了外部函数的变量并返回这个内部函数。defouter(x):# 外部函数definner(y):# 内部函数returnxy# 使用外部函数的变量 xreturninner# 返回内部函数# 创建一个闭包函数 add_fiveadd_fiveouter(5)# 使用闭包函数传入参数 10print(add_five(10))# 输出 15defcounter(start0):# 外部函数定义初始状态defincrement():# 内部函数改变并返回状态nonlocalstart# 使用外部函数的变量start1returnstartreturnincrement# 返回内部函数形成闭包# 创建一个计数器闭包counter1counter(5)print(counter1())# 输出 6print(counter1())# 输出 7print(counter1())# 输出 8装饰器是 Python 中非常常见的用法它本质上就是通过闭包的方式对一个函数进行包装从而在不改变函数本身的代码的情况下添加一些额外的功能。defdecorator(func):# 外部函数defwrapper():# 内部函数print(Before function call)func()# 调用原始函数print(After function call)returnwrapper# 返回内部函数decoratordefgreet():print(Hello!)greet()# 调用装饰过的函数解释decorator(func)是外部函数它接受一个函数func。wrapper()是内部函数它在调用func()前后增加了一些行为。decorator语法等价于greet decorator(greet)它将greet函数作为参数传递给decorator返回的wrapper函数替代了原始的greet函数。每次调用greet()时都会先打印“Before function call”然后执行原始的greet()最后打印“After function call”。lambda 表达式匿名函数lambda用于创建匿名函数的方法。通常lambda用于需要一个简单函数的场景尤其是在一些短小的回调函数或者函数式编程的应用中。lambdaarguments:expressionarguments函数的参数可以有多个用逗号分隔。也可没有参数。lambda: expressionexpression函数体的表达式lambda会返回该表达式的计算结果。使用场景在map()函数中使用lambdamap()函数会将指定函数应用到给定可迭代对象的每一个元素上返回一个结果的迭代器。通常可以与lambda一起使用以实现简单的转换。numbers[1,2,3,4]squaredmap(lambdax:x**2,numbers)print(list(squared))# 输出 [1, 4, 9, 16]在filter()函数中使用lambdafilter()函数用于筛选出符合条件的元素。lambda可以简化条件判断的书写。numbers[1,2,3,4,5,6]even_numbersfilter(lambdax:x%20,numbers)print(list(even_numbers))# 输出 [2, 4, 6]在sorted()函数中使用lambdasorted()可以通过key参数指定排序的标准而lambda函数通常用于定义复杂的排序规则。pairs[(1,one),(3,three),(2,two)]sorted_pairssorted(pairs,keylambdapair:pair[0])print(sorted_pairs)# 输出 [(1, one), (2, two), (3, three)]以上的map()、filter()、sorted()都是高阶函数。递归 Recursion递归函数直接或间接调用自身通常用于分治算法。需要有终止条件。deffactorial(n):# 递归求 n 的阶乘ifn1:return1returnn*factorial(n-1)装饰器 Decorators装饰器返回函数的函数用于扩展原函数的功能常用于日志、缓存、权限验证等。缓存计算结果的装饰器应用defcache(func):cached_results{}# 存储缓存的结果defwrapper(*args):ifargsincached_results:print(Returning cached result for,args)returncached_results[args]# 如果参数已经缓存直接返回缓存结果resultfunc(*args)# 否则计算结果cached_results[args]result# 缓存结果returnresultreturnwrapper# 示例函数计算斐波那契数列cachedeffibonacci(n):# 递归计算斐波那契数列ifn1:returnnreturnfibonacci(n-1)fibonacci(n-2)print(fibonacci(10))# 第一次计算缓存中没有结果print(fibonacci(10))# 第二次计算返回缓存的结果使用装饰器包裹递归计算斐波那契数列的函数并加上缓存功能。带有缓存的递归函数相当于递归有了记忆每次碰到相同的 n 就不用重复计算。正如那句名言那些忘记过去的人注定要重蹈覆辙。装饰器用于权限验证defrequires_permission(permission):defdecorator(func):defwrapper(user,*args,**kwargs):ifpermissionnotinuser[permissions]:raisePermissionError(fUser does not have {permission} permission)returnfunc(user,*args,**kwargs)returnwrapperreturndecorator# 假设有一个用户对象user{name:Alice,permissions:[read,write]}# 示例函数删除用户数据requires_permission(delete)defdelete_user(user):print(fUser{user[name]}deleted.)# 测试try:delete_user(user)# 用户没有 delete 权限会抛出异常exceptPermissionErrorase:print(e)# 为用户添加 delete 权限后再试user[permissions].append(delete)delete_user(user)# 正常执行带有参数的装饰器需要两层的函数调用外层用于接收参数内层用于接收被装饰的函数作为参数。requires_permission(delete)defdelete_user(user):print(fUser{user[name]}deleted.)相当于defdelete_user(user):print(fUser{user[name]}deleted.)requires_permission(delete)(delete_user)函数签名 Function Signature 与函数返回类型注解Python 提供了inspect模块来帮助我们查看函数的签名即函数参数列表定义部分。importinspectdefexample(a,b10,*args,**kwargs):pass# 查看函数签名print(inspect.signature(example))# 输出(a, b10, *args, **kwargs)Python 3 引入了函数注解Function Annotations允许在函数签名中注明参数类型和返回值类型。这些注解并不影响函数的运行但它们提供了额外的文档和类型信息有助于静态分析工具或开发者理解函数的预期行为。importinspectdefadd(a:int,b:int)-int:returnabdefexample(a:int,b:int10,*args,**kwargs)-str:returnsuccessprint(inspect.signature(example))可调用对象 Callable Objects可调用对象类实例通过实现__call__方法使得对象可以像函数一样被调用。classMyCallable:def__call__(self,name):returnfHello,{name}objMyCallable()print(obj(Alice))# 输出 Hello, Alice!动态函数动态创建函数在 Python 中动态函数指的是在程序运行时动态创建和执行的函数。与静态定义的函数不同动态函数是通过代码运行时的字符串输入来创建和执行的。Python 提供了exec()和eval()两个内置函数来支持动态创建和执行函数exec()exec()是 Python 中一个非常强大的内置函数它可以执行存储在字符串中的 Python 代码。用法exec()可以执行多行 Python 代码并且可以定义函数、类等。exec()执行的代码字符串必须是有效的 Python 代码。# 定义一个字符串该字符串包含要执行的 Python 代码code def dynamic_function(x): return x ** 2 # 使用 exec() 执行字符串中的代码动态定义函数exec(code)# 调用动态创建的函数print(dynamic_function(4))# 输出 16eval()eval()与exec()类似但它的功能有所不同。eval()用于执行单个表达式并返回表达式的值。它通常用于执行简单的表达式计算。用法eval()只接受一个单独的表达式并返回计算结果。eval()返回表达式的结果而不像exec()只是执行代码。# 动态计算表达式resulteval(3 5 * 2)print(result)# 输出 13和result 3 5 * 2的区别这两种情况下的最终结果都是13但它们在实现上有所不同。主要区别在于动态计算和静态计算的方式。静态计算在result 3 5 * 2中Python 直接计算表达式结果是13。这个表达式在代码编写时就已经确定下来Python 解释器直接对其进行求值并赋值给result。动态计算在result eval(3 5 * 2)中Python 运行时会首先将字符串3 5 * 2作为表达式传递给eval()然后eval()将其解析并计算。这里的表达式本身是在运行时而不是编写代码时确定的。eval()可以执行动态的字符串表达式因此它适用于那些需要在程序运行时从外部输入或构建的代码进行计算的场景。eval()会将传入的字符串当作 Python 表达式求值这使得它非常灵活。例如它允许用户输入动态的数学表达式或者根据程序的需求构造出不同的表达式进行计算。exec()和evel()允许程序运行时让用户输入代码进行执行但要慎用防止用户输入有害系统的代码语句。尾递归优化 Tail Call Optimization, TCO尾递归优化递归的最后一步直接返回结果Python 不支持尾递归优化但可以理解其原理。递归的基本原理是函数通过不断地调用自身在内存中的调用栈上压入当前函数的执行状态包括参数和局部变量等。递归过程会持续进行直到满足终止条件函数逐层返回并将调用栈中的函数状态逐一弹出返回给调用方。虽然在代码层面递归函数通常非常简洁但在内存管理层面它较为复杂特别是在递归深度较大或递归条件复杂时会显著增加内存开销并带来栈溢出的风险。尾递归优化正是为了解决这一问题而提出的优化技术。它通过在编译器或解释器层面识别递归函数中的尾调用并对其进行优化。具体来说尾递归优化可以消除递归调用过程中对栈帧的累积从而避免递归调用栈的持续增长防止栈溢出。在尾递归优化的帮助下递归调用的栈空间不会随着递归深度的增加而消耗更多内存提高了程序的效率和稳定性。普通递归函数求阶乘deffactorial(n):ifn1:return1else:returnn*factorial(n-1)# 递归调用发生在乘法运算之前尾递归优化求阶乘deffactorial_tail_recursive(n,accumulator1):ifn1:returnaccumulatorelse:returnfactorial_tail_recursive(n-1,accumulator*n)# 递归调用发生在函数的最后一步# 局部变量 accumulator 在递归调用的过程中保存了阶乘的值在递归终止条件满足时直接返回 accumulator 数值在尾递归中递归调用不需要保留当前的栈帧。也就是说递归调用时当前函数调用的栈帧可以被丢弃新的函数调用栈帧会覆盖在当前栈帧上从而节省内存。
Part 1:Python语言核心 - 函数
Python 函数 - Functions in Python函数的定义与调用定义函数使用def关键字来定义函数语法为deffunction_name(parameters):# 函数体returnvalue# 可选返回值函数调用函数通过函数名和参数来调用resultfunction_name(arguments)返回值函数使用return关键字返回值。一个函数只能返回一个值但 Python 可以通过元组来模拟多个返回值。defadd(x,y):returnxydefget_values():return1,2,3# 实际上是返回了一个元组 (1, 2, 3)# 通过解构赋值接收多个返回值a,b,cget_values()yieldyield用于生成器函数它返回一个迭代器而不是一次性返回所有结果。调用生成器函数时并不会立即执行函数内部的代码而是返回一个生成器对象。**生成器会在yield处暂停执行并记住当前的状态如变量值。下次迭代时它会从yield停止的地方继续执行而不是从头开始。**这意味着生成器不会一次性返回所有结果而是按需生成。这使得生成器特别适合处理大量数据或者需要延时计算的场景。defcount_up_to(limit):count1whilecountlimit:yieldcount count1fornumberincount_up_to(5):print(number)为什么使用yield而不是return?延迟计算yield允许按需生成结果而不是一次性返回所有的值。对于大规模数据处理生成器非常高效因为它只在需要时生成下一个值避免了内存占用。状态保持生成器保存了函数的状态。每次yield都会保存当前的执行状态变量值等使得下次从停止的位置继续执行而不是从头开始。惰性求值生成器是“惰性求值”的也就是说只有在请求下一个值时它才会计算并返回这使得它能够在内存有限的情况下处理大量数据。生成器也可配合next()内建函数使用defcount_up_to(limit):count1whilecountlimit:yieldcount count1# 创建生成器对象countercount_up_to(5)# 使用 next() 显式获取下一个生成值print(next(counter))# 输出 1print(next(counter))# 输出 2print(next(counter))# 输出 3print(next(counter))# 输出 4print(next(counter))# 输出 5参数系统参数传递对象引用对象引用传递Python 中的参数传递机制是 “对象引用传递”意味着函数接受的是对象的引用而不是副本。对于可变对象如列表、字典在函数内部对其修改会影响原对象。对于不可变对象如整数、字符串、元组函数内的修改不会影响原对象。defmodify_list(lst):lst.append(1)my_list[1,2,3,4]modify_list(my_list)print(my_list)# 输出 [1, 2, 3, 4, 1]位置参数Positional Arguments位置参数按位置顺序传递给函数。defgreet(name,age):print(fHello{name}, you are{age}years old.)greet(Alice,30)关键字参数Keyword Arguments关键字参数通过keyvalue方式显式传递避免位置错误。defgreet(name,age):print(fHello{name}, you are{age}years old.)greet(nameAlice,age30)默认参数Default Parameters默认值如果未传递某个参数则使用默认值。defgreet(name,age30):print(fHello{name}, you are{age}years old.)greet(Alice)# age 使用默认值 30可变默认参数陷阱避免使用可变对象作为默认参数因为它们在函数调用中会被共享。defappend_to_list(value,lst[]):lst.append(value)returnlst# 会发生意外的共享效果result1append_to_list(1)result2append_to_list(2)print(result1)# 输出 [1, 2]print(result2)# 输出 [1, 2]应避免此类问题解决方法使用None作为默认值并在函数内创建新的对象defappend_to_list(value,lstNone):iflstisNone:lst[]lst.append(value)returnlst*args可变位置参数可变位置参数使用*args来接收任意数量的位置参数返回一个元组。defsum_numbers(*args):print(type(args))# 输出 class tuplereturnsum(args)print(sum_numbers(1,2,3))# 输出 6**kwargs可变关键字参数可变关键字参数使用**kwargs来接收任意数量的关键字参数返回一个字典。defprint_details(**kwargs):forkey,valueinkwargs.items():print(f{key}:{value})print_details(nameAlice,age30)parameters 通常指定义函数时声明的形式参数形参。arguments 通常指函数调用传入的实际参数实参。仅位置参数/和仅关键字参数*仅位置参数/通过/来指定某些参数只能通过位置传递不能通过关键字传递。deffoo(a,b,/,c,d):# / 前面的是仅位置参数后面的可通过关键字传递print(a,b,c,d)foo(1,2,3,4)# 正确foo(a1,b2,c3,d4)# 错误a 和 b 不能作为关键字参数foo(1,2,c3,d4)# 正确a、b 仅位置参数c、d可作为关键字参数仅关键字参数*通过*来指定某些参数只能通过关键字传递也就是说这些参数不能通过位置传递只能显式地指定其名称。deffoo(a,b,*,c,d):# * 后面的是仅关键字参数前面的可通过关键字传递print(a,b,c,d)foo(1,2,c3,d4)# a、b 通过位置传递c、d 通过关键字传递foo(1,2,3,4)# 错误c,d 只能通过关键字传递foo(a1,b2,c3,d4)# 正确a,b 也可以通过关键字参数传递混合使用deffoo(a,b,/,c,d,*,e,f):print(a,b,c,d,e,f)在这个函数中a和b是仅位置参数只能通过位置传递。c和d是普通的位置参数可以通过位置或关键字传递。e和f是仅关键字参数只能通过关键字传递。高级函数作为对象函数是第一类对象函数可以作为参数传递、返回值、赋值给变量等。defgreet(name):returnfHello,{name}!say_hellogreetprint(say_hello(Alice))高阶函数高阶函数Higher-Order Function是指接受一个或多个函数作为参数或者返回一个函数的函数。换句话说高阶函数的特点是它能够操作函数使得函数的处理更加灵活和抽象。defgreet(name):returnfHello,{name}!defprocess_greeting(func,name):# 接受一个函数和一个名字作为参数并执行该函数returnfunc(name)# 传递 greet 函数作为参数resultprocess_greeting(greet,Alice)print(result)# 输出 Hello, Alice!defmultiply_by(n):defmultiplier(x):returnx*nreturnmultiplier# 创建一个函数该函数将数字乘以 2doublemultiply_by(2)# 调用返回的函数print(double(5))# 输出 10defouter(strParam):definner(func):print(Before function be called.)func(strParam)print(After function be called.)returninnerdefgreet(name):print(fHello,{name})outer(Alice)(greet)# 高阶函数的调用两次函数调用的链式组合闭包 Closure闭包是指一个内嵌函数即函数内部定义的函数引用了其外部函数的变量形成了对这些变量的闭合引用。换句话说闭包允许内嵌继续访问外部函数的局部变量即使外部函数已经执行完毕并返回。用途延迟计算闭包允许在外部函数执行时保存某些信息并在稍后的时间里通过内嵌函数访问和操作这些信息。状态封装它可以封装一些状态信息避免这些状态暴露给外部达到更好的数据封装和保护。闭包由两个部分组成外部函数定义外部环境变量通常是用于某些计算或状态。内部函数使用了外部函数的变量并返回这个内部函数。defouter(x):# 外部函数definner(y):# 内部函数returnxy# 使用外部函数的变量 xreturninner# 返回内部函数# 创建一个闭包函数 add_fiveadd_fiveouter(5)# 使用闭包函数传入参数 10print(add_five(10))# 输出 15defcounter(start0):# 外部函数定义初始状态defincrement():# 内部函数改变并返回状态nonlocalstart# 使用外部函数的变量start1returnstartreturnincrement# 返回内部函数形成闭包# 创建一个计数器闭包counter1counter(5)print(counter1())# 输出 6print(counter1())# 输出 7print(counter1())# 输出 8装饰器是 Python 中非常常见的用法它本质上就是通过闭包的方式对一个函数进行包装从而在不改变函数本身的代码的情况下添加一些额外的功能。defdecorator(func):# 外部函数defwrapper():# 内部函数print(Before function call)func()# 调用原始函数print(After function call)returnwrapper# 返回内部函数decoratordefgreet():print(Hello!)greet()# 调用装饰过的函数解释decorator(func)是外部函数它接受一个函数func。wrapper()是内部函数它在调用func()前后增加了一些行为。decorator语法等价于greet decorator(greet)它将greet函数作为参数传递给decorator返回的wrapper函数替代了原始的greet函数。每次调用greet()时都会先打印“Before function call”然后执行原始的greet()最后打印“After function call”。lambda 表达式匿名函数lambda用于创建匿名函数的方法。通常lambda用于需要一个简单函数的场景尤其是在一些短小的回调函数或者函数式编程的应用中。lambdaarguments:expressionarguments函数的参数可以有多个用逗号分隔。也可没有参数。lambda: expressionexpression函数体的表达式lambda会返回该表达式的计算结果。使用场景在map()函数中使用lambdamap()函数会将指定函数应用到给定可迭代对象的每一个元素上返回一个结果的迭代器。通常可以与lambda一起使用以实现简单的转换。numbers[1,2,3,4]squaredmap(lambdax:x**2,numbers)print(list(squared))# 输出 [1, 4, 9, 16]在filter()函数中使用lambdafilter()函数用于筛选出符合条件的元素。lambda可以简化条件判断的书写。numbers[1,2,3,4,5,6]even_numbersfilter(lambdax:x%20,numbers)print(list(even_numbers))# 输出 [2, 4, 6]在sorted()函数中使用lambdasorted()可以通过key参数指定排序的标准而lambda函数通常用于定义复杂的排序规则。pairs[(1,one),(3,three),(2,two)]sorted_pairssorted(pairs,keylambdapair:pair[0])print(sorted_pairs)# 输出 [(1, one), (2, two), (3, three)]以上的map()、filter()、sorted()都是高阶函数。递归 Recursion递归函数直接或间接调用自身通常用于分治算法。需要有终止条件。deffactorial(n):# 递归求 n 的阶乘ifn1:return1returnn*factorial(n-1)装饰器 Decorators装饰器返回函数的函数用于扩展原函数的功能常用于日志、缓存、权限验证等。缓存计算结果的装饰器应用defcache(func):cached_results{}# 存储缓存的结果defwrapper(*args):ifargsincached_results:print(Returning cached result for,args)returncached_results[args]# 如果参数已经缓存直接返回缓存结果resultfunc(*args)# 否则计算结果cached_results[args]result# 缓存结果returnresultreturnwrapper# 示例函数计算斐波那契数列cachedeffibonacci(n):# 递归计算斐波那契数列ifn1:returnnreturnfibonacci(n-1)fibonacci(n-2)print(fibonacci(10))# 第一次计算缓存中没有结果print(fibonacci(10))# 第二次计算返回缓存的结果使用装饰器包裹递归计算斐波那契数列的函数并加上缓存功能。带有缓存的递归函数相当于递归有了记忆每次碰到相同的 n 就不用重复计算。正如那句名言那些忘记过去的人注定要重蹈覆辙。装饰器用于权限验证defrequires_permission(permission):defdecorator(func):defwrapper(user,*args,**kwargs):ifpermissionnotinuser[permissions]:raisePermissionError(fUser does not have {permission} permission)returnfunc(user,*args,**kwargs)returnwrapperreturndecorator# 假设有一个用户对象user{name:Alice,permissions:[read,write]}# 示例函数删除用户数据requires_permission(delete)defdelete_user(user):print(fUser{user[name]}deleted.)# 测试try:delete_user(user)# 用户没有 delete 权限会抛出异常exceptPermissionErrorase:print(e)# 为用户添加 delete 权限后再试user[permissions].append(delete)delete_user(user)# 正常执行带有参数的装饰器需要两层的函数调用外层用于接收参数内层用于接收被装饰的函数作为参数。requires_permission(delete)defdelete_user(user):print(fUser{user[name]}deleted.)相当于defdelete_user(user):print(fUser{user[name]}deleted.)requires_permission(delete)(delete_user)函数签名 Function Signature 与函数返回类型注解Python 提供了inspect模块来帮助我们查看函数的签名即函数参数列表定义部分。importinspectdefexample(a,b10,*args,**kwargs):pass# 查看函数签名print(inspect.signature(example))# 输出(a, b10, *args, **kwargs)Python 3 引入了函数注解Function Annotations允许在函数签名中注明参数类型和返回值类型。这些注解并不影响函数的运行但它们提供了额外的文档和类型信息有助于静态分析工具或开发者理解函数的预期行为。importinspectdefadd(a:int,b:int)-int:returnabdefexample(a:int,b:int10,*args,**kwargs)-str:returnsuccessprint(inspect.signature(example))可调用对象 Callable Objects可调用对象类实例通过实现__call__方法使得对象可以像函数一样被调用。classMyCallable:def__call__(self,name):returnfHello,{name}objMyCallable()print(obj(Alice))# 输出 Hello, Alice!动态函数动态创建函数在 Python 中动态函数指的是在程序运行时动态创建和执行的函数。与静态定义的函数不同动态函数是通过代码运行时的字符串输入来创建和执行的。Python 提供了exec()和eval()两个内置函数来支持动态创建和执行函数exec()exec()是 Python 中一个非常强大的内置函数它可以执行存储在字符串中的 Python 代码。用法exec()可以执行多行 Python 代码并且可以定义函数、类等。exec()执行的代码字符串必须是有效的 Python 代码。# 定义一个字符串该字符串包含要执行的 Python 代码code def dynamic_function(x): return x ** 2 # 使用 exec() 执行字符串中的代码动态定义函数exec(code)# 调用动态创建的函数print(dynamic_function(4))# 输出 16eval()eval()与exec()类似但它的功能有所不同。eval()用于执行单个表达式并返回表达式的值。它通常用于执行简单的表达式计算。用法eval()只接受一个单独的表达式并返回计算结果。eval()返回表达式的结果而不像exec()只是执行代码。# 动态计算表达式resulteval(3 5 * 2)print(result)# 输出 13和result 3 5 * 2的区别这两种情况下的最终结果都是13但它们在实现上有所不同。主要区别在于动态计算和静态计算的方式。静态计算在result 3 5 * 2中Python 直接计算表达式结果是13。这个表达式在代码编写时就已经确定下来Python 解释器直接对其进行求值并赋值给result。动态计算在result eval(3 5 * 2)中Python 运行时会首先将字符串3 5 * 2作为表达式传递给eval()然后eval()将其解析并计算。这里的表达式本身是在运行时而不是编写代码时确定的。eval()可以执行动态的字符串表达式因此它适用于那些需要在程序运行时从外部输入或构建的代码进行计算的场景。eval()会将传入的字符串当作 Python 表达式求值这使得它非常灵活。例如它允许用户输入动态的数学表达式或者根据程序的需求构造出不同的表达式进行计算。exec()和evel()允许程序运行时让用户输入代码进行执行但要慎用防止用户输入有害系统的代码语句。尾递归优化 Tail Call Optimization, TCO尾递归优化递归的最后一步直接返回结果Python 不支持尾递归优化但可以理解其原理。递归的基本原理是函数通过不断地调用自身在内存中的调用栈上压入当前函数的执行状态包括参数和局部变量等。递归过程会持续进行直到满足终止条件函数逐层返回并将调用栈中的函数状态逐一弹出返回给调用方。虽然在代码层面递归函数通常非常简洁但在内存管理层面它较为复杂特别是在递归深度较大或递归条件复杂时会显著增加内存开销并带来栈溢出的风险。尾递归优化正是为了解决这一问题而提出的优化技术。它通过在编译器或解释器层面识别递归函数中的尾调用并对其进行优化。具体来说尾递归优化可以消除递归调用过程中对栈帧的累积从而避免递归调用栈的持续增长防止栈溢出。在尾递归优化的帮助下递归调用的栈空间不会随着递归深度的增加而消耗更多内存提高了程序的效率和稳定性。普通递归函数求阶乘deffactorial(n):ifn1:return1else:returnn*factorial(n-1)# 递归调用发生在乘法运算之前尾递归优化求阶乘deffactorial_tail_recursive(n,accumulator1):ifn1:returnaccumulatorelse:returnfactorial_tail_recursive(n-1,accumulator*n)# 递归调用发生在函数的最后一步# 局部变量 accumulator 在递归调用的过程中保存了阶乘的值在递归终止条件满足时直接返回 accumulator 数值在尾递归中递归调用不需要保留当前的栈帧。也就是说递归调用时当前函数调用的栈帧可以被丢弃新的函数调用栈帧会覆盖在当前栈帧上从而节省内存。