设为首页 加入收藏

TOP

函数的特殊使用方式(一)
2023-07-25 21:28:54 】 浏览:72
Tags:方式

5.4 函数的特殊使用方式

5.4.1 匿名函数

所谓匿名函数,即不再使用def语句这样标准形式定义的函数。Python中可以使用lambda关键字来创建匿名函数。用lambda创建的匿名函数的函数体比def定义的函数体要简单。语法如下:

lambda [参数1[,参数2],....参数n]]:表达式

lam_sum = lambda arg1, arg2: arg1 + arg2
print(lam_sum(10, 20))

30

上述代码中,第一行定义了一个lambda函数,执行两个数的和运算,并且把该lambda函数命名为lam_sum。然后通过lam_sum()函数实现求和的功能。
Lambda创建的匿名函数中只能封装有限的逻辑进去。
lambda函数拥有自己的命名空间,且不能访问自有参数列表之外或全局命名空间里的参数。
实际上,一般在使用匿名函数时是不会再为创建的匿名函数命名的。因为这样失去了匿名函数的简便性。在有些场景是需要传入函数,需要的逻辑并不是很复杂。但是又不想再创建一个,这个时候就可以直接使用匿名函数了。如下:

print(list(map(lambda x: x * x, [1, 2, 3, 4, 5])))

[1, 4, 9, 16, 25]

5.4.2 递归调用

在Python定义函数时,函数体中可以调用其他函数,甚至可以调用自己。这种自己调用自己的方式叫做递归调用。下面是一个递归式函数定义:

def recursion():
return recursion()

显然,对于上面定义的函数,如果你运行它,你将发现运行一段时间后,这个程序崩溃了(引发异常)。
从理论上说,这个程序将不断运行下去,但每次调用函数时,都将消耗一些内存。因此函数调用次数达到一定的程度(且之前的函数调用未返回)后,将耗尽所有的内存空间,导致程序终止并显示错误消息“超过最大递归深度(maximum recursion depth exceeded,默认最大为1000次)”。
可以通过以下代码修改最大递归深度:

import sys
sys.setrecursionlimit(99999)

这个函数中的递归称为无穷递归(就像以 while True 打头且不包含 break 和 return 语句的循环被称为无限循环一样),因为它从理论上说永远不会结束。你想要的是能对你有所帮助的递归函数,这样的递归函数通常包含下面两部分。
基线条件:满足这种条件时函数将直接返回一个值。
递归条件:包含一个或多个调用,这些调用旨在解决问题的一部分。
这里的关键是,通过将问题分解为较小的部分,可避免递归没完没了,因为问题终将被分解成基线条件可以解决的最小问题。
那么如何让函数调用自身呢?这没有看起来那么难懂。前面说过,每次调用函数时,都将为此创建一个新的命名空间。这意味着函数调用自身时,是两个不同的函数[更准确地说,是不同版本(即命名空间不同)的同一个函数]在交流。你可将此视为两个属于相同物种的动物在彼此交流。
递归示例1:通过递归的方式求一个数的阶乘

def factorial(p_int=0):
if p_int == 0:  # 基线条件
	    return 1
else:  #  递归条件
return p_int * factorial(p_int - 1)


print(factorial(10))

3628800

递归示例2:通过递归的方式求幂

def power(x, n):
    return 1 if n == 0 else x * power(x, n - 1)


print(power(2, 10))

1024

递归示例3:通过递归的方式解决汉诺塔问题

def move(n, a='A', b='B', c='C'):
    if n == 1:
        print('移动', a, '-->', c)
    else:
        move(n - 1, a, c, b)
        move(1, a, b, c)
        move(n - 1, b, a, c)


move(4)

移动 A --> B
移动 A --> C
移动 B --> C
移动 A --> B
移动 C --> A
移动 C --> B
移动 A --> B
移动 A --> C
移动 B --> C
移动 B --> A
移动 C --> A
移动 B --> C
移动 A --> B
移动 A --> C
移动 B --> C

在某些特殊的问题中,如果通过普通的循环方式虽然也可以实现,但在使用了递归的方式后代码更加简单。逻辑也更加清楚。
理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。
使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多会导致栈溢出。

5.4.3 偏函数

参考:偏函数
介绍函数参数的时候,我们讲到,通过设定参数的默认值,可以降低函数调用的难度。而偏函数也可以做到这一点。
int()函数可以把字符串转换为整数,当仅传入字符串时,int()函数默认按十进制转换:
>>> int('12345')

12345

但int()函数还提供额外的base参数,默认值为10。如果传入base参数,就可以做N进制的转换:
>>> int('12345', base=8)

5349

>>> int('12345', 16)

74565

假设要转换大量的二进制字符串,每次都传入int(x, base=2)非常麻烦,于是,我们想到,可以定义一个int2()的函数,默认把base=2传进去:

def int2(x, base=2):
    return int(x, base)

这样,我们转换二进制就非常方便了:

>>> int2('1000000')

64

>>> int2('1010101')

85

functools.partial就是帮助我们创建一个偏函数的,不需要我们自己定义int2(),可以直接使用下面的代码创建一个新的函数int2:
>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')

64

>>> int2('1010101')

85

所以,简单总结functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。
注意到上面的新的int2函数,仅仅是把base参数重新设定默认值为2,但也可以在函数调用时传入其他值:
>>> int2('1000000', base=10)

1000000

最后,创建偏函数时,实际上可以接收函数对象、*args和**kw这3个参数,当传入:
>>> int2 = functools.partial(int, base=2)
实际上固定了int()函数的关键字参数base,也就是:

>>> int2('10010')
相当于:
>>> kw = { 'base': 2 }
>>

首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Python unittest+ddt+openpyxl 下一篇【K哥爬虫普法】大数据风控第一案..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目