IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> Python学习日志10 - 高阶函数和高级应用 -> 正文阅读

[Python知识库]Python学习日志10 - 高阶函数和高级应用

Python学习日志

RBHGO的主页欢迎关注

温馨提示:创作不易,如有转载,注明出处,感谢配合~

目录

前言

上次分享了函数,我们还讲到过Python中常用的数据类型,这些类型的变量都可以作为函数的参数或返回值,用好函数还可以让我们做更多的事情。接触了函数我们得明白在写代码尤其是开发商业项目的时候,一定要有意识的将相对独立且重复出现的功能封装成函数,这样不管是自己还是团队的其他成员都可以通过调用函数的方式来使用这些功能。字符串是非常重要的数据类型,字符串的常用运算和方法需要掌握,因为一般的商业项目中,处理字符串比处理数值的操作要更多。前面也系统得分享字符串的知识字符串,有兴趣可以阅读一下。

开始正题之前,在和大家分享一个题。首先谈谈栈结构

函数调用会通过内存中称为“栈”(stack)的数据结构来保存当前代码的执行现场,函数调用结束后会通过这个栈结构恢复之前的执行现场。栈是一种先进后出的数据结构,这也就意味着最早入栈的函数最后才会返回,而最后入栈的函数会最先返回。

"""
设计一个函数,检查字符串 (算术表达式) 中的括号是否完全匹配。

算法:循环遍历字符串的每个字符,如果遇到左括号,将元素添加到栈中;
如果遇到右括号,就从栈中取出一个左括号,如果取不出左括号,直接返回 False;
如果能够取出左括号,检查二者是否匹配;如果不匹配,直接返回 False;
如果匹配,继续检查。循环遍历结束以后,检查栈是否为空,如果不为空,返回 False。

栈(stack) ---> FILO ---> 有一个列表,如果我们将添加和删除元素都限制在列表的末尾
    - 添加元素: append() ---> 入栈
    - 删除元素: pop() ---> 出栈
队列(queue) ---> --->FIF0---> 有一个列表,如果我们限制它只能在尾部添加元素,只能在头部删除元素
    - 添加元素: append() ---> 入队
    - 删除元素: pop(0) ---> 出队

"""


def check(expression: str) -> bool:
    """
    检查字符串中的括号是否完全匹配

    :param expression: 表达式
    :return: 括号匹配返回 True, 括号不匹配返回 False
    """
    stack = []
    bdict = {'(': ')', '[':']', '{': '}'}
    for ch in expression:
        if ch in ('(', '[', '{'):
            stack.append(ch)
        elif ch in (')', ']', '}'):
            if len(stack) > 0:
                left = stack.pop()
                if bdict[left] != ch:
                    return False
            else:
                return False
    return len(stack) == 0


if __name__ == '__main__':
    print(check('{{{)(}{}}[][]])('))
    print(check('{(12 - 10 = 10)}'))

进入正题

在面向对象的世界中,一切皆为对象,所以类和函数也是对象。函数的参数和返回值可以是任意类型的对象,这就意味着函数本身也可以作为函数的参数或返回值,这就是所谓的高阶函数

Python学习日志10课 - 高阶函数和高级应用

把函数作为函数的参数或者返回值。

在Python中的函数是一等函数(一等公民):

  1. 函数可以作为函数的参数
  2. 函数可以作为函数的返回值
  3. 函数可以直接赋值给变量

(1).递归方法

根据上面提到的高阶函数的定义,我们首先提出函数调用自身的情况。

  • 函数调用自己是以下这样一个过程:
    保存现场 —> 调用函数 —> 恢复现场
  • 每个函数都有属于自己的栈结构(大概512K-1M,因为要求要高效,所以不会很大),保存现场就是将整个栈结构保存起来

  • 结论︰函数可以调别人,也可以调自己,但是递归调用一定要确保能够快速收敛(尽快结束,不要无限制的调用),在有限次调用下结束调用,无休止的调用,那么迟早会将栈空间消耗殆尽,导致程序崩溃。

def fac(num: int) -> int:
    """求阶乘(递归写法)"""
    # 限制(收敛)条件,防止栈空间殆尽
    if num == 0:
        return 1
    # 递归调用(这里是在函数内直接调用自身)
    return num * fac(num - 1)

# __name__是一个隐藏变量,它代表了当前模块(文件)的名字
if __name__ == '__main__':
    for i in range(1, 21):
        print(fac(i))

如果直接通过Python解释器运行当前代码这个文件,那么__name__的值是’__main__‘。

但是如果是在其他的模块(文件)中导入了当前模块,那么此时__name__的值就是后面的模块

写函数时这样做就避免后面导入该模块运行程序时,会把导入模块的输出一起输出这种事情的出现。

小知识:

  1. 递归 —> 函数如果直接或间接的调用了自身,这种调用称为递归调用

  2. PyCharmmain然后按制表键,if __name__ == '__main__'这一段就会直接出来。

(2).高内聚低耦合

高阶函数除了函数本身也可以作为函数的参数或返回值这个特性之外,我认为实现了代码终极目标高内聚低耦合的函数,也能称之为高阶函数。

- **低内聚**:当把函数中的`print`、`二元运算符`、`循环`、`input`都减少甚至去掉,并且代码变得更易读,作用效果更大时
- **高耦合**:当把函数中的副作用清除的时候(如:要求程序进行排序输出元素下标,你若改变一段数据本身的顺序,那么下标也被改变)

例子1如下,其中init_value代表运算的初始值,op代表二元运算函数。

# operator内置绝大部分二元运算
import operator


# 添加一个op参数名 ---> 一个实现二元运算的函数(可以做任意的二元运算)
# 添加一个init_value参数名 ---> 给到初始值
def calculatec(init_value, op, *args, **kwargs):
    total = init_value
    for arg in args:
        if type(arg) in (int, float):
            total = op(total, arg)
    for value in kwargs.values():
        if type(value) in (int, float):
            total = op(total, value)
    return total


print(calculatec(0, operator.add, 11, 22, 33, 44))
print(calculatec(1, operator.mul, 11, 22, 33, 44))
print(calculatec(249, operator.sub, 11, 22, 33, 44))
print(calculatec(10000, operator.truediv, 11, 22, 33, 44))

通过对高阶函数的运用,calculatec函数不再和加法运算,乘法运算耦合,所以灵活性和通用性会变强。需要注意的是,将函数作为参数和调用函数是有显著的区别的,调用函数需要在函数名后面跟上圆括号,而把函数作为参数时只需要函数名即可。Python内置函数中有不少高阶函数,我们前面提到过的map函数就是高阶函数,map函数可以实现对序列中元素的映射。

例子2如下,函数一开时对列表进行全切片,相当于复制了一份代码来进行排序,后面的输出索引返回的时原列表的下标。

"""
冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字#3母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。
"""

def bubble_sort(items, ascending=True):
    # 复制(全切片)原列表,程序运行不影响原列表
    # 设计函数的时候,一定要注意函数的无副作用性(调用函数不影响调用者)
    items = items[:]
    for i in range(1, len(items)):
        swapped = False
        for j in range(0, len(items) - i):
            if items[j] > items[j + 1]:
                items[j], items[j + 1] = items[j + 1], items[j]
                swapped = True
        if not swapped:
            break
        if not ascending:
            items = items[::-1]
    return items


if __name__ == '__main__':
    nums = [35, 96, 12, 78, 56, 64, 39, 80]
    print(nums)
    print(bubble_sort(nums))

(3).Lambda函数

Lambda函数 —> Python中的Lambda函数是没有的名字函数,所以很多人也把它叫做匿名函数,而且它是用一句话就能写完的函数,唯一的表达式就是函数的返回值。在使用高阶函数的时候,如果作为参数或者返回值的函数本身非常简单,一行代码就能够完成,这时候我们就可以使用Lambda函数。

# example02 - 编写实现对列表元素进行冒泡排序的函数

def bubble_sort(items, ascending=True, gt=lambda x, y: x > y):
    """
    对列表元素进行冒泡排序

    :param items: 待排序的列表
    :param ascending: 是否使用升序
    :param gt: 比较两个元素大小的函数
    :return: 返回冒泡排序后的列表
    """
    # 复制(全切片)原列表,程序运行不影响原列表
    # 设计函数的时候,一定要注意函数的无副作用性(调用函数不影响调用者)
    items = items[:]
    for i in range(1, len(items)):
        swapped = False
        for j in range(0, len(items) - i):
            if gt(items[j], items[j + 1]):
                items[j], items[j + 1] = items[j + 1], items[j]
                swapped = True
        if not swapped:
            break
    if not ascending:
        items = items[::-1]
    return items


if __name__ == '__main__':
    nums = [35, 96, 12, 78, 56, 64, 39, 80]
    letters = ['pink', 'ggbond', 'rainbow', 'zoo', 'internationalization']
    print(nums)
    print(bubble_sort(nums))
    print(bubble_sort(letters, gt=lambda x, y: len(x) > len(y), ascending=False))

还可以对(2)中的例子1用Lambda实现

def calculatec(*args, init_value, op, **kwargs):
    total = init_value
    for arg in args:
        if type(arg) in (int, float):
            total = op(total, arg)
    for value in kwargs.values():
        if type(value) in (int, float):
            total = op(total, value)
    return total

# 定义函数时,写在*前面的参数称为位置参数,调用函数传递参数时,只需要对号入座
# 写在*后面的参数称为命名关键字参数,调用函数传递参数时,必须要写成"参数名=参数值"的形式
print(calculatec(25, init_value=100, op=lambda x, y: x // y))

有很多函数在Python中用一行代码就能实现,我们可以用Lambda函数来定义这些函数,调用Lambda函数就跟调用普通函数一样,代码如下所示。

import operator, functools

# 一行代码定义求阶乘的函数
fac = lambda num: functools.reduce(operator.mul, range(1, num + 1), 1)
# 一行代码定义判断素数的函数
is_prime = lambda x: x > 1 and all(map(lambda f: x % f, range(2, int(x ** 0.5) + 1)))

# 调用Lambda函数
print(fac(10))        			# 运行结果:3628800
print(is_prime(9))    			# 运行结果:False

提示1:上面使用的reduce函数是Python标准库functools模块中的函数,它可以实现对数据的归约操作,通常情况下,过滤(filter)、映射(map)和归约(reduce)是处理数据中非常关键的三个步骤,而Python的标准库也提供了对这三个操作的支持。

提示2:上面使用的all函数是Python内置函数,如果传入的序列中所有布尔值都是Trueall函数就返回True,否则all函数就返回False

小练习

斐波那契数列的递归方法实现

def fib(n):
    """斐波那契数列的实现(递归方法"""
    if n in (1, 2):
        return 1
    return fib(n - 1) + fib(n - 2)


if __name__ == '__main__':
    for i in range(1, 21):
        print(fib(i))

其实这样做效率很低,不如直接写,但是主要是想让大家了解递归这个函数的高阶应用。

总结

函数的递归调用一定要注意收敛条件和递归公式,找到递归公式才有机会使用递归调用,而收敛条件确定了递归什么时候停下来。函数调用通过内存中的栈空间来保存现场和恢复现场,栈空间通常都很小,所以递归如果不能迅速收敛,很可能会引发栈溢出错误,从而导致程序的崩溃。Python中的函数也是对象,所以函数可以作为函数的参数和返回值,也就是说,在Python中我们可以使用高阶函数。写程序要尽量保证做到高内聚低耦合,不与python内置耦合同时保证程序无副作用。如果我们要定义的函数非常简单,只有一行代码且不需要名字,可以将函数写成Lambda函数(匿名函数)的形式。

感谢学习陪伴,您的点赞,评论就是我更新的动力

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2021-08-08 11:17:17  更:2021-08-08 11:18:28 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/26 1:01:57-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计