测开之函数进阶篇・第五篇《递归函数纯函数匿名函数偏函数》

Posted 七月的小尾巴

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了测开之函数进阶篇・第五篇《递归函数纯函数匿名函数偏函数》相关的知识,希望对你有一定的参考价值。

递归函数

递归函数的定义

在函数中调用函数自身,我们把这样的函数叫做递归函数

定义递归函数

# 通过递归函数实现阶乘
def fun(n):
    if n == 1: # 递归临界点(不再调用函数自身)
        return 1
    else:
        return n * fun(n -1)

递归函数的优点是定义简单, 逻辑清晰. 理论上, 所有的递归函数都可以写成循环的方式, 但循环的逻辑不如递归清晰。使用递归函数需要注意防止栈溢出. 在计算机中, 函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧, 每当函数返回, 栈就会减少一层栈帧. 由于栈的大小不是无限的, 所以, 递归调用的次数过多, 会导致栈溢出. 当尝试调用fact(1000)时, 程序会报错。

总结来说,使用递归函数的优点是逻辑简单清晰,缺点是过深的调用会导致栈溢出(即比较占用内存空间).

纯函数

纯函数的概念

简单来说,一个函数的返回结果只依赖于它的参数,并且在执行过程里面没后副作用,我们把这个函数叫做纯函数。

纯函数的3个原则

  1. 变量都只在函数作用域内获取,作为函数的参数传入
  2. 不会产生副作用(side effects),不会改变被传入的数据或者其他数据(全局变量)
  3. 相同的输入保证相同的输出

函数的副作用

副作用是指函数被调用,完成了函数既定的计算任务,但是同时因为访问了外部数据,尤其是因为对数据进行了写操作,从而一定程序的改变了系统环境。

python常用的内置函数

  • map函数:会根据提供的函数对执行序列做映射

    str()是python的内置函数,这个例子是把列表/元组/字符串的每个元素变成了str类型,然后以列表的形式返回。

     a = list(map(str,'python'))
    结果:
    ['p', 'y', 't', 'h', 'o', 'n']
    

    定义一个函数,使用add方法,将两个列表相加

    def add(x, y):
    
        return x+y
    
    
    list1 = [1, 2, 3]
    
    list2 = [4, 5, 6]
    
    a = list(map(add, list1, list2))
    
    print(a)
    
  • filter 函数
    filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回一个迭代器对象,如果要转换为列表,可以使用 list() 来转换。

    该接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判断,然后返回 True 或 False,最后将返回 True 的元素放到新列表中。

    下面我们来看filter函数的源代码:

    def filter(function_or_none, sequence): # known special case of filter
        """
        filter(function or None, sequence) -> list, tuple, or string
        
        Return those items of sequence for which function(item) is true.  If
        function is None, return the items that are true.  If sequence is a tuple
        or string, return the same type, else return a list.
        """
        pass
    

    使用这个函数,我们需要传两个参数。

    1. function_or_none:判断函数,可以传函数名称或者None
    2. sequence:可迭代对象

    上面这么说可能有写抽象,下面我们来看一下实例

    #!/usr/bin/python3
     
    def is_odd(n):
    	# 判断返回奇数
        return n % 2 == 1
     
    # 使用 filter进行过滤
    tmplist = filter(is_odd, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
    newlist = list(tmplist)
    print(newlist)
    
    结果:
    [1, 3, 5, 7, 9]
    
    
    • zip函数

    zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。

    如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同,利用 * 号操作符,可以将元组解压为列表。

    # 打包为元祖列表
    test = zip([1, 2, 3], [2, 3, 4])
    
    返回:
    [(1, 2), (2, 3), (3, 4)]
    

匿名函数

匿名函数值适用于简单的函数定义。
下面我们来定义一个简单的匿名函数

res = lambda a, b: a + b
print(res(1, 10))

结果:
11

上方的代码,等同于我们我们下方的函数

def test(a, b):
    return a + b

但是python中并不知道我们上方通过这个写法去使用lambda函数,匿名函数的定义是函数即用即内存释放。我们上方 res 相当于定义了一个函数,但是这个写法res一直会在内存中,没有被释放。

在python中,匿名函数通常都会结合 filter、map进行使用,如下代码:

li = [1, 2, 22, 30, 55]
res = filter(lambda x: x < 10, li)
print(res)

返回:
[1, 2]

匿名函数还可以结合表达式使用:

res = [(lambda x: x % 5 == 0)(i) for i in range(10)]

三步运算符

假设我们需要判断如下代码

if a>b:
    max = a;
else:
    max = b;

可以使用三步 表达式完成

max = a if a>b else b

三步表达式和lambda函数一样,可以使我们代码变的简洁。

偏函数

偏函数的定义

在python内置模块functools提供了很多有用的功能,其中一个就是偏函数(partial)。当函数参数太多,需要简化,使用 functools.partial 可以创建一个新函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。

偏函数的案例

在我们前面学到的内置函数 filter 中,调用的时候需要传入两个参数,第一个是函数,第二个是我们需要过滤的可迭代类型的数据。

我们可以通过传入不同的过滤条件来过来出来我们需要的数据

li1 = [1, 20, 30, 100, 1001]
filter(lambda x: x > 3, li1)

上方代码中,我们只是过来一个列表的数据,但是如果有多组列表的数据都要过滤呢?我们应该怎么处理?
难道我们要像下面这样,每次过滤一组数据都用lambda函数吗?


li1 = [1, 20, 30, 100, 1001]
li2 = [1, 20, 30, 100, 1001]
li3 = [1, 20, 30, 100, 1001]
li4 = [1, 20, 30, 100, 1001]

filter(lambda x: x > 3, li1)
filter(lambda x: x > 3, li2)
filter(lambda x: x > 3, li3)
filter(lambda x: x > 3, li4)

我们可以再functools.partial 可以创建一个新函数,通过偏函数创建一个新函数,提前传入原函数所需要的参数,让我们在调用的时候更加简单。

from functools import partial

filter2 = partial(filter, lambda x:x>5)
print(list(filter2(li1)))

以上是关于测开之函数进阶篇・第五篇《递归函数纯函数匿名函数偏函数》的主要内容,如果未能解决你的问题,请参考以下文章

测开之函数进阶篇・第六篇《闭包》

测开之函数进阶篇・第七篇《装饰器》

测开之面向对象进阶篇・《多态》

测开之并发编程篇・《并发并行线程队列进程和协程》

测开之面向对象进阶篇・《属性自省》

测开之面向对象进阶篇・《魔术方法》