上接 python 函数式编程学习笔记

参考:www.sigai.cn/

1 函数式编程概述

  • 前提:函数在 Python 中是⼀等对象
  • 工具:built-in ⾼阶函数;lambda 函数;operator 模块;functools 模块
  • 模式:闭包与装饰器
  • 替代:⽤用 List Comprehension 可轻松替代 map 和 filter(reduce 替代起来⽐比较困难)
  • 原则:No Side Effect

何为 No Side Effect? 函数的所有功能就仅仅是返回一个新的值而已,没有其他行为,尤其是不得修改外部变量。因⽽,各个独⽴的部分的执⾏顺序可以随意打乱,带来执⾏顺序上的⾃自使得⼀系列新的特性得以实现:⽆锁的并发;惰性求值;编译器器级别的性能优化等

1.1 程序的状态与命令式编程

  • 程序的状态首先包含了当前定义的全部变量
  • 有了程序的状态,我们的程序才能不断往前推进
  • 命令式编程,就是通过不断修改变量的值,来保存当前运⾏的状态,来步步推进

1.2 函数式编程

  • 通过函数来保存程序的状态(通过函数创建新的参数和返回值来保存状态)
  • 函数一层层的叠加起来,每个函数的参数或返回值代表了⼀个中间状态
  • 命令式编程⾥一次变量值的修改,在函数式编程⾥变成了⼀个函数的转换
  • 最自然的方式:递归

2 一等函数

一等对象的定义:

  • 在运⾏时创建
  • 能赋值给变量或数据结构中的元素
  • 能作为参数传给函数
  • 能作为函数的返回结果

Python 中,所有函数的都是一等对象,简称为一等函数

2.1 高阶函数

定义:接受函数为参数,或把函数作为返回结果的函数

2.1.1 map 映射

map() 是 Python 内置的高阶函数,它接收一个函数 f 和一个可迭代对象,并通过把函数 f 依次作用在 可迭代对象 的每个元素上,并返回一个新的可迭代对象。


def f(x):
return x * x print('map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])):',
list(map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])))

show:

map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])): [1, 4, 9, 16, 25, 36, 49, 64, 81]

替代方案:

[x*x for x in range(1,10)]

def format_name(s):
s1 = s[0:1].upper() + s[1:].lower()
return s1 print("map(format_name, ['adam', 'LISA', 'barT']):",
list(map(format_name, ['adam', 'LISA', 'barT'])))
map(format_name, ['adam', 'LISA', 'barT']): ['Adam', 'Lisa', 'Bart']

替代方案:

[format_name(name) for i, name in enumerate(['adam', 'LISA', 'barT'])]

因而,列表推导可以很好的替换 map 函数。

2.1.2 filter 过滤器

x = [(), [], {}, None, '', False, 0, True, 1, 2, -3]

x_result = filter(bool, x)
list(x_result)
[True, 1, 2, -3]

替代方案:

[i for i in x if bool(i)]

print("filter((lambda x: x>0), range(-5, 5)):",
list(filter((lambda x: x > 0), range(-5, 5))))
filter((lambda x: x>0), range(-5, 5)): [1, 2, 3, 4]

替代方案:

[x for x in range(-5, 5) if x > 0]

2.1.3 reduce 递推

from functools import reduce

m = 2
n = 5
reduce(lambda x, y: x * y, list(range(1, n + 1)), m)
240

def multiply(a, b):
return a * b reduce(multiply, range(1, 5))
24

2.1.4 zip 并行

print("zip( [1, 2, 3], [4, 5, 6]):", list(zip([1, 2, 3], [4, 5, 6])))

show:

zip( [1, 2, 3], [4, 5, 6]): [(1, 4), (2, 5), (3, 6)]

2.1.5 sorted 排序

>>> sorted([x * (-1) ** x for x in range(10)])
[-9, -7, -5, -3, -1, 0, 2, 4, 6, 8]
>>> sorted([x * (-1) ** x for x in range(10)], reverse=True)
[8, 6, 4, 2, 0, -1, -3, -5, -7, -9]
>>> sorted([x * (-1) ** x for x in range(10)], key=abs)
 [0, -1, 2, -3, 4, -5, 6, -7, 8, -9]
>>> sorted([x * (-1) ** x for x in range(10)], reverse=True, key=abs)
[-9, 8, -7, 6, -5, 4, -3, 2, -1, 0]

minmax 同理。

2.2 partial

functools 这货用于高阶函数:指那些作用于函数或者返回其他函数的函数。通常情况下,只要是可以被当做函数调用的对象就是这个模块的目标。

假设有如下函数:

def multiply(x, y):
return x * y

现在,我们想返回某个数的双倍,即:


>>> multiply(3, y=2)
6
>>> multiply(4, y=2)
8
>>> multiply(5, y=2)
10

上面的调用有点繁琐,每次都要传入 y=2,我们想到可以定义一个新的函数,把 y=2 作为默认值,即:

def double(x, y=2):
return multiply(x, y)

现在,我们可以这样调用了:


>>> double(3)
6
>>> double(4)
8
>>> double(5)
10

事实上,我们可以不用自己定义 double,利用 partial,我们可以这样:

from functools import partial

double = partial(multiply, y=2)

partial 接收函数 multiply 作为参数,固定 multiply 的参数 y=2,并返回一个新的函数给 double,这跟我们自己定义 double 函数的效果是一样的。

所以,简单而言,partial 函数的功能就是:把一个函数的某些参数给固定住,返回一个新的函数。

需要注意的是,我们上面是固定了 multiply 的关键字参数 y=2,如果直接使用:

double = partial(multiply, 2)

2 是赋给了 multiply 最左边的参数 x


from functools import partial

def subtraction(x, y):
return x - y f = partial(subtraction, 4) # 4 赋给了 x
>>> f(10)   # 4 - 10
-6

组合高阶函数:


from functools import partial

abs_sorted = partial(sorted, key=abs)
abs_sorted([x * (-1) ** x for x in range(10)])

show:

[0, -1, 2, -3, 4, -5, 6, -7, 8, -9]
abs_reverse_sorted = partial(sorted, key=abs, reverse=True)
abs_reverse_sorted([x * (-1) ** x for x in range(10)])

show:

[-9, 8, -7, 6, -5, 4, -3, 2, -1, 0]

2.3 匿名函数

  • 定义:使⽤用 lambda 表达式创建的函数,函数本身没有名字
  • 特点:只能使⽤用纯表达式,不能赋值,不能使⽤用 whiletry 等块语句
  • 语法: lambda [arg1 [,arg2 [,arg3]]]: expression

Expressions get a value; Statements do something

lambda & def

写法上:

  1. def 可以用代码块,一个代码块包含多个语句
  2. lambda只能⽤单行表达式,⽽表达式仅仅是单个语句中的⼀种

结果上:

  1. def 语句一定会增加⼀个函数名称
  2. lambda 不会,这就降低了了变量名污染的⻛险

能用一个表达式直接放到 return 里返回的函数都可以⽤ lambda 速写

def multiply(a, b):
return a * b multiply_by_lambda = lambda x,y: x * y

List + lambda 可以得到⾏为列表

f_list = [lambda x: x + 1, lambda x: x ** 2, lambda x: x ** 3]
[f_list[j](10) for j in range(3)]
[11, 100, 1000]

在 AI 领域里,这种写法常用于处理数据,比如按预定的⼀系列模式处理数据

下面我们以两个例子来结束高阶函数:

例1:


L = range(6)

# 计算l中每个元素的两倍和平方,并将两种组成一个列表
# lambda表达式和python函数一样,也可以接受函数作为参数 def twoTimes(x):
return x * 2 def square(x):
return x**2 print([list(map(lambda x: x(i), [twoTimes, square])) for i in L])
print(list(filter(lambda x: x % 2 == 0, L))) # 内置reduce函数,计算 L 的和
print(reduce(lambda accumValue, newValue: accumValue + newValue, L, 0))
[[0, 0], [2, 1], [4, 4], [6, 9], [8, 16], [10, 25]]
[0, 2, 4]
15

我们依然可以使用列表解析的方式替换 map & filter

[[twoTimes(x), square(x)] for x in L]

[x for x in L if x % 2 == 0]

通过上面的例子我们发现,使用列表推导要比 map 与 filter 简洁且易于理解得多。

但是,我们这里还有一个惰性计算的坑:


f_list = [lambda x:x**i for i in range(5)]

[f_list[j](3) for j in range(5)]
[81, 81, 81, 81, 81]

大家可以思考为什么会出现这个意想不到的结果?


函数修饰器(Decorator)其他相关知识

Python中的函数是对象,故函数可以被当做变量使用。

参考资料:

作用:

对已有的函数添加一些小功能,却又不希望对函数内容有太多的刚性的修改。

  • 将需要添加功能的函数像普通对象一样作为参数传入修饰器在中;
  • 将函数作为修饰器的返回值返回。

修饰器的本质: Python解释器在发现函数调用修饰器后,将其传入修饰器中,然后用返回的函数对象将自身完全替换。

一个函数可以被多个修饰器嵌套修饰:

@deco3
@deco2
@deco1
def df():
pass

等同于 f=deco3(deco2(deco1(f)))

示例

添加水印 Hello World_

def deco(func):          #修饰器deco
def wrappedFunc(): #内嵌函数wrappedFunc(所有对于传入函数的修饰逻辑都将在此内嵌函数中实现。)
return 'Hello World_'+func()
return wrappedFunc # 在程序中若有函数需要修饰器修饰,只需在函数定义前使用“`@+修饰器名`”即可使用此修饰器。
@deco
def f(): # 调用修饰器
return 'I am f'
def g(): # 没有调用修饰器
return 'I am g' print(f())
print(g())
Hello World_I am f
I am g
def deco(f):
def g():
return [f()[i] for i in range(5)]
return g
@deco
def h():
return [1,2,3,4,56,7,'75s']
print(h())
[1, 2, 3, 4, 56]

函数式编程之 Python的更多相关文章

  1. Python函数式编程之map()

    Python函数式编程之map() Python中map().filter().reduce()这三个都是应用于序列的内置函数. 格式: map(func, seq1[, seq2,…]) 第一个参数 ...

  2. Python函数式编程之lambda表达式

    一:匿名函数的定义 lambda parameter_list: expression 二:三元表达式 条件为真时返回的结果 if 条件判断 else 条件为假的时候返回的结果 三:map map(f ...

  3. python 函数式编程之lambda( ), map( ), reduce( ), filter( )

    lambda( ), map( ), reduce( ), filter( ) 1. lambda( )主要用于“行内函数”: f = lambda x : x + 2 #定义函数f(x)=x+2 g ...

  4. python函数式编程之yield表达式形式

    先来看一个例子 def foo(): print("starting...") while True: res = yield print("res:",res ...

  5. python3 第二十章 - 函数式编程之Higher-order function(高阶函数)

    什么是高阶函数?把函数作为参数传入或把函数做为结果值返回,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式.函数式编程的特点: 函数本身可以赋值给变量,赋值后变量为函数: 允许将函数本身 ...

  6. python3 第二十三章 - 函数式编程之Partial function(偏函数)

    要注意,这里的偏函数和数学意义上的偏函数不一样,偏函数是2.5版本以后引进来的东西,属于函数式编程的一部分.前面章节中我们讲到,通过设定参数的默认值,可以降低函数调用的难度.而偏函数也可以做到这一点. ...

  7. 函数式编程之pipeline——很酷有没有

    Pipeline pipeline 管道借鉴于Unix Shell的管道操作——把若干个命令串起来,前面命令的输出成为后面命令的输入,如此完成一个流式计算.(注:管道绝对是一个伟大的发明,他的设哲学就 ...

  8. python3 第二十二章 - 函数式编程之Decorator(装饰器)

    前面我们说了,在python中,一切皆对象.函数也是一个对象,而且函数对象可以被赋值给变量,通过变量也能调用该函数.如: def sayHello(name): print(name + ' hell ...

  9. python3 第二十一章 - 函数式编程之return函数和闭包

    我们来实现一个可变参数的求和.通常情况下,求和的函数是这样定义的: def calc_sum(*args): ax = 0 for n in args: ax = ax + n return ax 但 ...

随机推荐

  1. Python的虚拟环境virtualenv

    原文地址:blog.sina.com.cn/s/blog_4ddef8f80101eu0w.html Python的虚拟环境可以使一个Python程序拥有独立的库library和解释器interpre ...

  2. Python 入门基础19 --面向对象、封装

    2019.04.17 一.面向对象与面向过程 二.名称空间操作 三.类与对象的概念 四.语法 五.对象查找属性的顺序 2019.04.18 1.类与对象的所有概念:__init__方法 2.类的方法与 ...

  3. try 、catch 、finally 、throw 测试js错误

    try语句允许我们定义在执行时进行错误测试的代码块. catch 语句允许我们定义当 try 代码块发生错误时,所执行的代码块. finally 语句在 try 和 catch 之后无论有无异常都会执 ...

  4. 一步步使用Code::Blocks进行设置断点调试程序

    一.调试之前要做的工作 首先,我们要确保Code::Blocks的配置正确,调试工作才能进行得更顺利 为此,我们需要生成调试符号.调试符号可以让调试器知道代码的哪一行正在执行,这样你就可以知道程序运行 ...

  5. [Fedora 20] 设置Terminal快捷键 + 设置桌面快捷方式 + Terminal透明解决方案

    一.设置Terminal快捷键 刚安装Fedora的时候,习惯性的按Ctrl+Alt+T可是终端怎么都不出来,这才意识到Fedora和Ubuntu是不一样的,于是自己设置快捷键 1.进入All set ...

  6. win7安装Ubuntu变双系统以及删除Ubuntu分区操作

    Window7系统基础上安装Ubuntu使构成双系统,整个过程如下: 1. 一块空闲磁盘分区准备. “我的电脑”右键 > 管理 > 磁盘管理 > 压缩(从有空余分区压缩)/删除(删除 ...

  7. Vue.js——循环(Java、JSTL标签库、数据库)

    一.Vue.js循环 Vue.js循环要使用 v-for 指令. v-for 指令需要以 student in StudentList 形式的特殊语法使用, StudentList 是源数据数组并且s ...

  8. elasticsearch5.0.1集群一次误删除kibana索引引发的血案

    elasticsearch集群中一次删除kibana索引引发的血案 1.问题发生的过程: 早上的时候有某个索引无法看到报表数据,于是就点该报表多次,估计集群被点挂了,报错:Elasticsearch ...

  9. tomcat8配置SSL

    参考网址:http://www.micmiu.com/enterprise-app/sso/sso-cas-sample/#viewSource 1.生成证书 keytool -genkey -ali ...

  10. TCP/IP五层层次模型

    TCP/IP层次模型共分为五层:应用层HTTP.传输层TCP.网络层IP.数据链路层Data-link.物理层physical.·         应用层-应用层是所有用户所面向的应用程序的统称.IC ...