Python-函数式编程(高阶函数、匿名函数、返回函数、装饰器)_python高阶函数-程序员宅基地

技术标签: python  

目录

简介

高阶函数

map

filter

匿名函数

返回函数

闭包

装饰器

双装饰器

装饰器模板

思考题

参考


简介

函数式编程是种编程范式(函数式编程程序编程面向对象编程指令式编程等)。函数编程语言最重要的基础是λ演算(lambda calculus),而且λ演算的函数可以接受函数当作输入(参数)和输出(返回值)。

函数式编程关心数据的映射,命令式编程关心解决问题的步骤

这里的映射就是数学上「函数」的概念——一种东西和另一种东西之间的对应关系。

这也是为什么「函数式编程」叫做「函数」式编程。(知乎——什么是函数式编程思维

高阶函数

高阶函数是至少满足下列一个条件的函数:

  • 接受一个或多个函数作为输入

  • 输出一个函数

在看下面的内容之前,你需要转变一下思想:

函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。

为了让读者更清晰地理解,在上一篇文章Python-函数基础总结与内置函数中,将print函数赋值给了变量display,类似于函数指针或者说给print函数起了个别名。

>>> display
<built-in function print>

map

map(functioniterable...)

返回一个将 function 应用于 iterable 中每一项并输出其结果的迭代器。 如果传入了额外的 iterable 参数,function 必须接受相同个数的实参并被应用于从所有可迭代对象中并行获取的项。 当有多个可迭代对象时,最短的可迭代对象耗尽则整个迭代就将结束。

仍以上一篇的函数为例:

def power(x, n=2):
    return x ** n

如果需要一个1~10的平方组成的列表,你会怎么做呢?for循环,调用power,添加到列表中?使用map一行就搞定了。如果是立方呢?请看下面:

t = (range(1, 11))
n = (3, 3, 3, 3, 3, 3, 3, 3, 3, 3)
res = map(power, t)
print(type(res))
print(list(res))
res = map(power, t, n)
print(list(res))

结果:

<class 'map'>
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]

python3没有reduce?不,它被放到了functools模块中,需要导入。这是为什么呢?博主也很好奇,于是去找了一下,原来不止是reduce,python语言的设计者吉多·范罗苏姆(Guido van Rossum)甚至想将lambda、reduce、filter、map函数都删除。

吉多·范罗苏姆(Guido van Rossum)的部分回信内容

最后他只从标准库中删除了reduce,并放到了functools里面。他不想自己设计的语言引入其他语言中抽象复杂的部分,python更多的是作为一种面向对象的脚本语言,而不是函数式编程语言。

参考:The fate of reduce() in Python 3000

filter

filter(functioniterable)

用 iterable 中 传入函数 function返回真的那些元素,构建一个新的迭代器。iterable 可以是一个序列,一个支持迭代的容器,或一个迭代器。如果 function 是 None ,则会假设它是一个身份函数,即 iterable 中所有返回假的元素会被移除。

一句话:利用函数对序列进行过滤/筛选,如同名字一样。

得到一个列表包含100以内的所有素数:

def prime(num):
    tmp = math.sqrt(num)
    i = 2
    while i <= tmp:
        if num % i == 0:
            return False
        i += 1
    return True
t = (range(2, 100))
res = filter(prime, t)
print(type(res))
print(list(res))

结果:

<class 'filter'>
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]

大多数情况下,列表生成式可以完成map和filter的功能。

map 相当于[F(x) for x in S],filter相当于for x in S if P(x)]

t = (range(1, 11))
l = [power(x) for x in t]
l = [power(x,3) for x in t]
t = (range(2, 100))
l = [x for x in t if prime(x)]

有关列表更多内容,可查看:Python-列表总结(操作符、方法、内置函数、相关模块)

匿名函数

一些时候只会在一个地方调用一个很简单的函数,没必要将他们使用def显示的定义出来,这个时候就用到了匿名函数。

Lambda函数可以在需要函数对象的任何地方使用。它们在语法上限于单个表达式。语法如下:

lambda [arg1,arg2,....]:expression

中括号代表可选,也就是说Lambda函数可以有>=0个参数

对于上一篇文章的plus函数:

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

可以写成:

lambda a,b:a+b

对于power函数,可以写成:

lambda x:x**2

同样,lambda可以作为参数传给其他函数,例如,传给map:

res = map(lambda x: x ** 2, t)

返回函数

说完了函数作为参数,我们就来说说函数作为返回值,也就是返回一个函数。

既然返回一个函数,我们就需要在一个函数中再定义一个函数,有种套娃的感觉。

def plus_not_now(*t):
    def plus_all():
        return sum(t)

    return plus_all


tp = (range(1, 6))
fun = plus_not_now(*tp)
print(type(fun))
print(fun())

我们在plus_not_now中又定义了一个函数plus_all,并将它作为结果返回(注意,不是返回plus_all())。

结果:

<class 'function'>
15

调用函数fun时才真正运行plus_all的内容。

这里有个奇怪的事情,我们在上面的程序后面再定义一个变量fun2,打印一下两个id

fun2 = plus_not_now(*tp)
print(id(fun))
print(id(fun2))

结果:

<class 'function'>
15
2005949437416
2005950885320

怎么会不一样呢?这个问题就作为本篇文章的思考题吧!

闭包

当一个内嵌函数引用其外部作用域的变量,我们就会得到一个闭包。创建一个闭包必须满足以下几点:

  1. 必须有一个内嵌函数
  2. 内嵌函数必须引用外部函数中的变量
  3. 外部函数的返回值必须是内嵌函数

前面的返回函数就是闭包

注意,尽量不要引用变化变量

def plus():
    funcs = []
    for i in range(3):
        def add():
            return i + i
        funcs.append(add)
    return funcs


if __name__ == '__main__':
    p1, p2, p3 = plus()
    print(p1(), p2(), p3())

结果为

4 4 4

等你运行p1时,i已经变为了2,不是0,当然,你可以将参数绑定:

def plus():
    funcs = []

    def bind(j):
        def add():
            return j + j

        return add

    for i in range(3):
        funcs.append(bind(i))
    return funcs

装饰器

装饰器:在代码运行前后动态增扩展功能的方式

目前有一些业务已经上线了,现在需要增加验证功能

def add(a, b):
    print(a + b)


def multi(a, b):
    print(a * b)


def devision(a, b):
    print(a / b)

你可能这样写,但如果不让你修改已上线业务代码呢?

def calling(name):
    print(f'{name}正在被调用...')


def add(a, b):
    calling('add')
    print(a + b)


def multi(a, b):
    calling('multi')
    print(a * b)


def devision(a, b):
    calling('devision')
    print(a / b)

你可以这样

def addcalling(func):
    def calling(a, b):
        print(f'{getattr(func, "__name__")}正在被调用...')
        func(a, b)

    return calling


def add(a, b):
    print(a + b)


def multi(a, b):
    print(a * b)


def devision(a, b):
    print(a / b)


if __name__ == '__main__':
    add = addcalling(add)
    add(3, 5)

函数指向了内部函数,multi这些也可以,当然,Python中使用 @装饰器 即可自动让装饰器将下面的函数传入并返回装饰后的函数

def addcalling(func):
    def calling(a, b):
        print(f'{getattr(func, "__name__")}正在被调用...')
        func(a, b)

    return calling

@addcalling
def add(a, b):
    print(a + b)

@addcalling
def multi(a, b):
    print(a * b)

@addcalling
def devision(a, b):
    print(a / b)


if __name__ == '__main__':
    add(3, 5)
    multi(3, 5)
    devision(3, 5)

@addcalling  等价于 func = addcalling(func),所以,即使不运行add(3,5),在解释器解释python代码的过程中碰到@addcalling也会运行addcalling函数

双装饰器

在上面的基础上,再添加一个计算函数运行时间的装饰器

import time


def addcalling(func):
    def calling(a, b):
        print(f'{getattr(func, "__name__")}正在被调用...')
        func(a, b)

    return calling


def addtime(func):
    def runtime(a, b):
        starttime = time.time()
        func(a, b)
        endtime = time.time()
        print(f'运行时间{endtime - starttime}')

    return runtime

@addtime
@addcalling
def add(a, b):
    print(a + b)

@addtime
@addcalling
def multi(a, b):
    print(a * b)

@addtime
@addcalling
def devision(a, b):
    print(a / b)


if __name__ == '__main__':
    add(3, 5)
    multi(3, 5)
    devision(3, 5)

addtime虽然写在前面,但其实是先用addcalling装饰后再用的addtime,类似栈,可以在addtime的时候输出一下函数名

def addtime(func):
    def runtime(a, b):
        starttime = time.time()
        func(a, b)
        print(getattr(func, "__name__"))
        endtime = time.time()
        print(f'运行时间{endtime - starttime}')

结果

add正在被调用...
8
calling
运行时间0.0

装饰器模板

def wrapper_args(*args, **kwargs):
    def wrapper(func):
        # 装饰前需要运行的语句
        def inner(*args, **kwargs):
            # 被装饰函数运行前需要执行的语句
            ret = func(*args, **kwargs)
            # 被装饰函数运行后,得到返回值前需要执行的语句
            return ret

        # 装饰后,返回函数前需要运行的语句
        return inner

    return wrapper

 例如,在Flask等框架中有@app.route(“/”),来添加路由,这就是含参装饰器,上面应该是装饰器的很全的版本了。

或者使用类实现装饰器

class Wrapper(object):
    def __init__(self, func):
        self.__func = func

    def __call__(self, *args, **kwargs):
        # 被装饰函数运行前需要执行的语句
        ret = self.__func(*args, **kwargs)
        # 被装饰函数运行后,得到返回值前需要执行的语句
        return ret

 装饰器的应用:

  • 计算函数运行时间
  • 添加函数运行日志
  • 权限校验
  • 实现类的单例模式

思考题

1.上一篇文章我把print函数赋值给了一个变量display

>>> display=print
>>> id(print)
1777190314296
>>> id(display)
1777190314296

它们的id是一样的,而在上面的返回函数中,fun与fun2的id是不一样的,请思考下为什么不一样?

参考

Python官方文档-函数式编程指引

更多python相关内容:【python总结】python学习框架梳理

本人b站账号:lady_killer9

有问题请下方评论,转载请注明出处,并附有原文链接,谢谢!如有侵权,请及时联系。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/lady_killer9/article/details/108943582

智能推荐

mysql dwith boost_linux下Mysql 8.0.19 编译安装-程序员宅基地

文章浏览阅读951次。1 前言linux下安装MySQL的方式有很多种,包括以仓库的方式安装(yum,apt,zypper),以包的方式安装(rpm,deb),以docker方式安装,从压缩包解压安装,从源码编译安装,这里使用的是最后一种,从源码编译安装。编译安装需要大量的耐心与时间,而且还会遇到非常多奇奇怪怪的问题,因此,需要极大的毅力,很有可能一万次失败也换不来一次的成功,请做好心理准备。2 准备工作下面是安装要求..._mysql dwith boost

mysql 高级(进阶学习)_mysql高级进阶-程序员宅基地

文章浏览阅读1.4k次,点赞2次,收藏8次。视图就是将某个查询语句存储在数据中,并为其命名,视图中并不存储数据,数据还是在基本表中存储。定义视图使用视图删除视图存储过程就是把一段处理逻辑存入到数据库中,使用是就由 JDBC 调用即可。调用存储过程可以减少应用程序和数据库交互次数,在数据库内部执行,执行效率高。存储事先需要定义,有三种参数类型:in 入参(接收调用者传入的数据)out 返回(向调用者返回数据)inout (既可以接收调用者传入的数据,也可以向调用者返回数据)函数是一个特殊的存储过程。存储过程不仅有输入参数,还有输出参数,但是没有返回值,_mysql高级进阶

goquery php,golang:Goquery简单爬虫实例-程序员宅基地

文章浏览阅读189次。Selection类型提供的方法,这些方法是页面解析最重要,最核心的方法1)类似函数的位置操作-Eq(indexint)*Selection//根据索引获取某个节点集-First()*Selection//获取第一个子节点集-Last()*Selection//获取最后一个子节点集-Next()*Selection..._goquery获取tbody的数据

计算机资源库在哪,电脑的资源管理在哪里-程序员宅基地

文章浏览阅读2.7k次。语音内容:大家好,我是时间财富网智能客服时间君,上述问题将由我为大家进行解答。电脑的资源管理的位置:1、单击开始菜单,在弹出的快捷菜单中选择文件资源管理器。2、按组合键Win+R打开运行窗口。3、在运行窗口中输入命令:explorer按回车键执行命令即可以打开资源管理器窗口。4、在桌面的任务栏上右击鼠标,在弹出的快捷菜单中选择“任务管理器。5、在任务管理器的菜单栏中选择文件中运行新任务。6、在运行..._计算机库到哪里找

适合新手的CTF靶场合集-程序员宅基地

文章浏览阅读3.4w次,点赞42次,收藏344次。前言我整理得没有那么全,这里的合集主要还是面对新手。做题贵精不在多,好好练习每一题,学习每个知识点,不懂的百度或者 Google 即可。记住,你是为了提高自己而去打 CTF 。CTF 比赛时间表CTFwiki(入门必看wiki): https://ctf-wiki.github.io/ctf-wiki/#/introduction XCTF社区: https://time.xctf...._ctf靶场

彻底理解位运算——左移、右移_左移和右移-程序员宅基地

文章浏览阅读6.2w次,点赞168次,收藏598次。相信大家在各种语言各种框架中都能看到二进制的操作。左移、右移、&、|、^等等操作。那么这篇帖子让各位彻底弄懂左移、右移。首先先区分那个是左移、那个是右移,这很简单,从箭头指向的方向来区分。右移左移:很简单的来说就是把当前的二进制,整体往左边移动N个单位,N取决于你的表达式。那么用一个例子,和画图来理解一下吧。32 ..._左移和右移

随便推点

Freescale I.mx 6 Android 4.2.2源码编译环境搭建(基于ubuntu12.04 LTS)_note, selecting 'lib32z1-dev' instead of 'lib32z-d-程序员宅基地

文章浏览阅读2.9k次。Freescale I.mx 6 Android 4.2.2源码编译环境搭建 1 安装必要的第三方工具:$ sudo apt-get install git gnupg flex bison gperf build-essential \ zip curl zlib1g-dev libc6-dev lib32ncurses5-dev ia32-libs \ x11proto-_note, selecting 'lib32z1-dev' instead of 'lib32z-dev

大数据存储架构详解:数据仓库、数据集市、数据湖、数据网格、湖仓一体_数据仓库 数据集市 数据湖-程序员宅基地

文章浏览阅读1.3w次,点赞7次,收藏75次。本文以文字+思维导图+表格的形式详解了数据库、数据仓库、数据集市、数据湖、数据网格、湖仓一体之间的区别。_数据仓库 数据集市 数据湖

如何运用python多线程threading实现程序的并发_python 如何实现并发获取数据-程序员宅基地

文章浏览阅读443次。每次技术的进步都是面对问题解决问题,有了现实中需要解决的问题了我们才能想各种方法解决他也就成就了技术的跃迁。_python 如何实现并发获取数据

柴天佑pdf 自适应控制_串讲:控制理论:自适应控制(APC)-程序员宅基地

文章浏览阅读4k次,点赞3次,收藏21次。自适应控制 (APC)说道自适应控制(APC),也要追溯到5年前第一次接触,当时还只会应用下面的自适应律公式来求解,这里结合自己的一些想法来对自适应控制进行深入剖析,希望可以帮助到大家。APC的历史:在早期的二十世纪五十年代,APC被开始研究,当时应用在飞机的自动导航装置上。简而言之,APC是一种带有在线参数识别的控制方法,主要可以被分为模型参考自适应控制(MRAC)、自校正控制器(STC)、参数..._自适应控制 柴天佑pdf

浅谈Overlay File System的应用_filesystem overlay-程序员宅基地

文章浏览阅读6.7k次,点赞3次,收藏18次。Overlay FS在Docker中的使用_filesystem overlay

职场法则-高效沟通_双五十理论-程序员宅基地

文章浏览阅读802次。高效沟通在职场上,我们能遇到向上沟通,平行沟通,向下沟通,这其中的沟通就显得尤为重要,这是我学习过程中一个同事写的,我拿来做笔记记录下来,保持一个高效的沟通,才能在职场上走得更远。1、何为沟通?沟通就是无论用任何的方式交换(有传递、有反馈)信息的过程。著名的双50%理论在工作中有50%以上的时间都用在了沟通上。如开会、谈判、指示、评估。可是,工作中的50%以上的障碍都是在沟通中产生的。沟通的本质是价值的交换2、沟通的类别传递方式语言沟通语言沟通是指用语言符号进行的信息交流,包括_双五十理论

推荐文章

热门文章

相关标签