python 高阶函数

news2024/11/17 11:34:51

传入函数

要理解“函数本身也可以作为参数传入”,可以从Python内建的map/reduce函数入手。

我们先看map。map()函数接收两个参数,一个是函数,一个是序列,map将传入的函数依次作用到序列的每个元素,并把结果作为新的list返回。

举例说明,比如我们有一个函数f(x)=x2,要把这个函数作用在一个list [1, 2, 3, 4, 5, 6, 7, 8, 9]上,就可以用map()实现如下:

 

现在,我们用Python代码实现:

>>> def f(x):
...     return x * x
...
>>> map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
[1, 4, 9, 16, 25, 36, 49, 64, 81]

请注意我们定义的函数f。当我们写f时,指的是函数对象本身,当我们写f(1)时,指的是调用f函数,并传入参数1,期待返回结果1。

因此,map()传入的第一个参数是f,即函数对象本身。

map()函数这种能够接收函数作为参数的函数,称之为高阶函数(Higher-order function)。

你可能会想,不需要map()函数,写一个循环,也可以计算出结果:

L = []
for n in [1, 2, 3, 4, 5, 6, 7, 8, 9]:
    L.append(f(n))
print L

的确可以,但是,从上面的循环代码,能一眼看明白“把f(x)作用在list的每一个元素并把结果生成一个新的list”吗?

所以,map()作为高阶函数,事实上它把运算规则抽象了,因此,我们不但可以计算简单的f(x)=x2,还可以计算任意复杂的函数。

再看reduce的用法。reduce把一个函数作用在一个序列[x1, x2, x3...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

比方说对一个序列求和,就可以用reduce实现:

>>> def add(x, y):
...     return x + y
...
>>> reduce(add, [1, 3, 5, 7, 9])
25

当然求和运算可以直接用Python内建函数sum(),没必要动用reduce。

但是如果要把序列[1, 3, 5, 7, 9]变换成整数13579,reduce就可以派上用场:

>>> def fn(x, y):
...     return x * 10 + y
...
>>> reduce(fn, [1, 3, 5, 7, 9])
13579

这个例子本身没多大用处,但是,如果考虑到字符串str也是一个序列,对上面的例子稍加改动,配合map(),我们就可以写出把str转换为int的函数:

>>> def fn(x, y):
...     return x * 10 + y
...
>>> def char2num(s):
...     return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
...
>>> reduce(fn, map(char2num, '13579'))
13579

整理成一个str2int的函数就是:

def str2int(s):
    def fn(x, y):
        return x * 10 + y
    def char2num(s):
        return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
    return reduce(fn, map(char2num, s))

还可以用lambda函数进一步简化成:

def char2num(s):
    return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]

def str2int(s):
    return reduce(lambda x,y: x*10+y, map(char2num, s))

也就是说,假设Python没有提供int()函数,你完全可以自己写一个把字符串转化为整数的函数,而且只需要几行代码!

lambda函数的用法在下一节介绍。

排序算法

排序也是在程序中经常用到的算法。无论使用冒泡排序还是快速排序,排序的核心是比较两个元素的大小。如果是数字,我们可以直接比较,但如果是字符串或者两个dict呢?直接比较数学上的大小是没有意义的,因此,比较的过程必须通过函数抽象出来。通常规定,对于两个元素xy,如果认为x < y,则返回-1,如果认为x == y,则返回0,如果认为x > y,则返回1,这样,排序算法就不用关心具体的比较过程,而是根据比较结果直接排序。

Python内置的sorted()函数就可以对list进行排序:

>>> sorted([36, 5, 12, 9, 21])
[5, 9, 12, 21, 36]

此外,sorted()函数也是一个高阶函数,它还可以接收一个比较函数来实现自定义的排序。比如,如果要倒序排序,我们就可以自定义一个reversed_cmp函数:

def reversed_cmp(x, y):
    if x > y:
        return -1
    if x < y:
        return 1
    return 0

传入自定义的比较函数reversed_cmp,就可以实现倒序排序:

>>> sorted([36, 5, 12, 9, 21], reversed_cmp)
[36, 21, 12, 9, 5]

我们再看一个字符串排序的例子:

>>> sorted(['about', 'bob', 'Zoo', 'Credit'])
['Credit', 'Zoo', 'about', 'bob']

默认情况下,对字符串排序,是按照ASCII的大小比较的,由于'Z' < 'a',结果,大写字母Z会排在小写字母a的前面。

现在,我们提出排序应该忽略大小写,按照字母序排序。要实现这个算法,不必对现有代码大加改动,只要我们能定义出忽略大小写的比较算法就可以:

def cmp_ignore_case(s1, s2):
    u1 = s1.upper()
    u2 = s2.upper()
    if u1 < u2:
        return -1
    if u1 > u2:
        return 1
    return 0

忽略大小写来比较两个字符串,实际上就是先把字符串都变成大写(或者都变成小写),再比较。

这样,我们给sorted传入上述比较函数,即可实现忽略大小写的排序:

>>> sorted(['about', 'bob', 'Zoo', 'Credit'], cmp_ignore_case)
['about', 'bob', 'Credit', 'Zoo']

从上述例子可以看出,高阶函数的抽象能力是非常强大的,而且,核心代码可以保持得非常简洁。

函数作为返回值

高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。

我们来实现一个可变参数的求和。通常情况下,求和的函数是这样定义的:

def calc_sum(*args):
    ax = 0
    for n in args:
        ax = ax + n
    return ax

但是,如果不需要立刻求和,而是在后面的代码中,根据需要再计算怎么办?可以不返回求和的结果,而是返回求和的函数!

def lazy_sum(*args):
    def sum():
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum

当我们调用lazy_sum()时,返回的并不是求和结果,而是求和函数:

>>> f = lazy_sum(1, 3, 5, 7, 9)
>>> f
<function sum at 0x10452f668>

调用函数f时,才真正计算求和的结果:

>>> f()
25

在这个例子中,我们在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。

请再注意一点,当我们调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数:

>>> f1 = lazy_sum(1, 3, 5, 7, 9)
>>> f2 = lazy_sum(1, 3, 5, 7, 9)
>>> f1==f2
False

f1()f2()的调用结果互不影响。

小结

把函数作为参数传入,或者把函数作为返回值返回,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式。

假设Python没有提供map()函数,请自行编写一个my_map()函数实现与map()相同的功能。

Python提供的sum()函数可以接受一个list并求和,请编写一个prod()函数,可以接受一个list并利用reduce()求积。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/184375.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java:基于注解的Spring使用【AOP容器】和事务管理

目录 第十五章 AOP前奏15.1 代理模式15.2 为什么需要代理【程序中】15.3 手动实现动态代理环境搭建15.4 手动实现动态代理关键步骤第十六章 Spring中AOP【重点】16.1 AspectJ框架【AOP框架】16.2 使用AspectJ步骤&#xff08;入门&#xff09;16.3 Spring中AOP概述16.4 Spring中…

AMQP 0-9-1 模型解释

官方文档链接&#xff1a;https://www.rabbitmq.com/tutorials/amqp-concepts.html 文章目录1. AMQP协议是什么2. AMQP模型2.1 工作过程2.2 深入理解3. 交换机3.1 默认交换机3.2 直连交换机3.3 扇形交换机3.4 主题交换机3.5 头交换机3.6 交换机小结4. Queue队列队列属性队列创建…

BM7 链表中环的入口结点

目录 描述 输入描述&#xff1a; 返回值描述&#xff1a; 示例1 示例2 示例3 思路&#xff1a; 代码 描述 给一个长度为n链表&#xff0c;若其中包含环&#xff0c;请找出该链表的环的入口结点&#xff0c;否则&#xff0c;返回null。 例如&#xff0c;输入{1,…

DW 2023年1月Free Excel 第九次打卡 Excel数据透视

第九章 Excel数据透视 数据下载地址与参考链接&#xff1a;https://d9ty988ekq.feishu.cn/docx/Wdqld1mVroyTJmxicTTcrfXYnDd 数据透视是Excel中个强大的数据处理和分析工具&#xff0c;能够快速实现数据的汇总与统计分析&#xff0c;本节重点讲解Excel数据透视的相关操作。 1…

NSSCTF Round#7 Team ez_rce和0o0讲解

强烈建议NSSCTF延长时间&#xff0c;大过年的逛亲戚回来就剩两个小时了。。。。 ez_rce <!-- A EZ RCE IN REALWORLD _ FROM CHINA.TW --> <!-- By 探姬 --> <?PHPif(!isset($_POST["action"]) && !isset($_POST["data"]))show_s…

MySQL8中jdbc的url设置

JDBC spring.datasource.urljdbc:mysql://${MYSQL_HOST:localhost}:3306/xxxx?sslModeREQUIRED&characterEncodingUTF-8&connectionTimeZoneGMT%2B8&forceConnectionTimeZoneToSessiontruesslMode:设置为REQUIRED表示必须启用ssl加密传输&#xff1b;characterEn…

svn客户端add无法添加上子文件夹及其子文件——解决办法

1、问题描述 svn客户端add文件夹后&#xff0c;无法添加上子文件夹及其子文件&#xff0c;需要先add最外层文件夹&#xff0c;再逐层add子文件夹&#xff0c;最后add最里层子文件夹中的文件&#xff0c;很影响add速度啊。现象如下图所示&#xff1a; 正常情况下&#xff0c;add…

公派访问学者申请优势有哪些?

人的一生&#xff0c;若从职业生涯论&#xff0c;无非为官、为学、为商三条路。为官者&#xff0c;出国访学一年半载&#xff0c;对仕途并无太大作用&#xff0c;并且在此期间有可能丧失国内提拔的大好机会;为学者&#xff0c;公派访问学者是对学术水平的认可&#xff0c;并且对…

vue.js 实现导入json解析成动态el-table树表格(接口文档功能)

一、需求描述&#xff1a;前段时间接到一个需求是做一个类似接口文档的显示功能&#xff0c;将一段json数据贴到里面就可以自动解析出json数据的每个字段的类型和层级关系&#xff0c;用element组件的树表格的形式展示&#xff0c;并且可以手动新增、修改和删除某个数据字段。二…

Vue路由和路由器简介

前言 路由(route)是vue中非常重要的技术&#xff0c;几乎每一个用vue所写的项目都会用到路由&#xff0c;它是一个vue的插件库&#xff0c;专门实现SPA应用 路由(route)的简介 说到路由&#xff0c;大多数人会想到路由器(router),可以这么说&#xff0c;路由器上的每一个口都…

Python类变量和实例变量

类变量&#xff08;类属性&#xff09;类变量指的是在类中&#xff0c;但在各个类方法外定义的变量。举个例子&#xff1a;class CLanguage : # 下面定义了2个类变量name "CSDN社区"add "http://csdn.net" # 下面定义了一个say实例方法 defsay(self, conte…

【Linux】进程信号的产生与捕捉、核心转储

目录 一、信号的引入 二、信号捕捉 三、核心转储 四、系统调用发送信号 五、软件条件产生信号 六、硬件异常产生信号 一、信号的引入 Linux信号本质是一种通知机制&#xff0c;用户 or 操作系统通过发送一定的信号&#xff0c;通知进程&#xff0c;某些事件已经发生&…

JavaWeb-MyBatis | Mapper代理开发及案例

本专栏主要是记录学习完JavaSE后学习JavaWeb部分的一些知识点总结以及遇到的一些问题等&#xff0c;如果刚开始学习Java的小伙伴可以点击下方连接查看专栏 本专栏地址&#xff1a;&#x1f525;JDBC Java入门篇&#xff1a; &#x1f525;Java基础学习篇 Java进阶学习篇&#x…

【5G NTN】5G NTN(非地面组网)介绍

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…

DTD语法详细介绍

在编写XML文档时&#xff0c;需要掌握XML语法。同理&#xff0c;在编写DTD文档时&#xff0c;也需要遵循一定的语法。DTD的结构一般由元素类型定义、属性定义、实体定义、记号(notation)定义等构成&#xff0c;一个典型的文档类型定义会把将来要创建的XML文档的元素结构、属性类…

LeetCode Hot100 ---- 双指针算法专题

167, 88, 142, 76双指针双指针法&#xff0c;有时也叫快慢指针&#xff0c;在数组里是用两个整型值代表下标&#xff0c;在链表里是两个指针&#xff0c;一般能实现O(n)的时间解决问题&#xff0c;两个指针的位置一般在第一个元素和第二个元素或者第一个元素和最后一个元素&…

【Leetcode每日一题】844. 比较含退格的字符串|重构字符串/双指针

博主简介&#xff1a;努力学习的预备程序媛一枚~博主主页&#xff1a; 是瑶瑶子啦所属专栏: LeetCode每日一题–进击大厂 前言&#xff1a; 昨天的【Leetcode每日一题】27. 原地移除元素|神级理解双指针一文中&#xff0c;生动形象的为大家讲解如何理解双指针&#xff0c;受到…

《收获,不止Oracle》表的连接学以致用

6.2 三大类型 6.2.1 连接类型 嵌套循环 排序合并及散列连接 第一种方式就是数据库表连接中的嵌套循环连接&#xff08;Nested Loops Join&#xff09;&#xff0c;而第二种方式就是表连接中的排序合并连接&#xff08;Merge Sort Join&#xff09;或者散列连接&#xff08;…

【SpringCloud】Gateway服务网关的基本使用

一、初识Gateway服务网关为什么需要网关&#xff1f;在微服务中&#xff0c;各个模块之间的调用&#xff0c;也可以称其为远程调用&#xff01;但是&#xff0c;如果是外部&#xff08;用户&#xff09;对微服务进行访问时&#xff0c;发的请求能不加处理的直接访问微服务吗&am…

使用Client Java构建Exporter程序

一、client_java client_java是Prometheus针对JVM类开发语言的client library库&#xff0c;我们可以直接基于client_java用户可以快速实现独立运行的Exporter程序&#xff0c;也可以在我们的项目源码中集成client_java以支持Prometheus。注意&#xff1a;Prometheus 提供的cl…