python编程语言基础测试三_Python基础学习笔记(3)

1.函数式编程

函数是Python内建支持的一种封装,我们通过把大段代码拆成函数,通过一层一层的函数调用,就可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计。

越低级的语言,越贴近计算机,抽象程度低,执行效率高,比如C语言;越高级的语言,越贴近计算,抽象程度高,执行效率低,比如Lisp语言。

22856f8d3c7059ff427c5b989b348aac.png

上图中语言越往上越高级,抽象程度越高。越往上抽象就逐渐向数学接近。

函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。

函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!

函数式编程的特点:

把计算视为函数而非指令(这样不贴近计算机而是贴进计算)

纯函数式编程: 不需要变量,没有副作用,测试简单。(函数任意执行多少次结果确定)

支持高阶函数,代码简洁

python支持的函数式编程

不是纯函数式编程:允许有变量

支持高阶函数:函数也可以作为变量传入

支持闭包:有了闭包就能返回函数

有限度的支持匿名函数。

函数式编程:

最主要的特征是,函数是第一等公民。

强调将计算过程分解成可复用的函数,典型例子就是map方法和reduce方法组合而成 MapReduce 算法。

只有纯的、没有副作用的函数,才是合格的函数。

2.高阶函数

高阶函数:

变量可以指向函数

函数名其实就是指向函数的变量

高阶函数:能接收函数做参数的函数(因为函数的参数可以接收变量,而变量可以指向函数,所以一个函数可以接收另一个函数作为参数。)

把函数本身赋值给变量:

>>> f = abs

>>> f

结论:函数本身也可以赋值给变量,即:变量可以指向函数。

如果一个变量指向了一个函数,那么,可否通过该变量来调用这个函数?用代码验证一下:

>>> f = abs

>>> f(-10)

10

成功!说明变量f现在已经指向了abs函数本身。直接调用abs()函数和调用变量f()完全相同。

函数名也是变量:

那么函数名是什么呢?函数名其实就是指向函数的变量!对于abs()这个函数,完全可以把函数名abs看成变量,它指向一个可以计算绝对值的函数!

如果把abs指向其他对象,会有什么情况发生?

>>> abs = 10

>>> abs(-10)

Traceback (most recent call last):

File "", line 1, in

TypeError: 'int' object is not callable

把abs指向10后,就无法通过abs(-10)调用该函数了!因为abs这个变量已经不指向求绝对值函数而是指向一个整数10!

当然实际代码绝对不能这么写,这里是为了说明函数名abs也是变量,指向绝对值函数,当然也可以指向其他的。要恢复abs函数,请重启Python交互环境。

传入函数参数:

既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。

一个最简单的高阶函数:

def add(x, y, f):

return f(x) + f(y)

当我们调用add(-5, 6, abs)时,参数x,y和f分别接收-5,6和abs,根据函数定义,我们可以推导计算过程为:

x = -5

y = 6

f = abs

f(x) + f(y) ==> abs(-5) + abs(6) ==> 11

return 11

为什么我们要将函数作为参数,不直接内部调用函数?

因为把函数作为参数可以增强代码的复用性,如果在函数内直接调用,相当于只能求平方和,而将平方函数作为参数的话,也可以将开平方,三次方等函数作为参数,增加复用性。

map:

Python内建了map()和reduce()函数。

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

>>> def f(x):

...     return x * x

...

>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])

>>> list(r)

[1, 4, 9, 16, 25, 36, 49, 64, 81]

>>> list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9]))

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

-- 输入:['adam', 'LISA', 'barT'] 输出:['Adam', 'Lisa', 'Bart'],使用map

def format_name(s):

return s[0].upper() + s[1:].lower()

print map(format_name, ['adam', 'LISA', 'barT'])

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

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

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

>>> from functools import reduce

>>> def add(x, y):

...     return x + y

...

>>> reduce(add, [1, 3, 5, 7, 9])

25

调用 reduce(f, [1, 3, 5, 7, 9])时,reduce函数将做如下计算:

先计算头两个元素:f(1, 3),结果为4;

再把结果和第3个元素计算:f(4, 5),结果为9;

再把结果和第4个元素计算:f(9, 7),结果为16;

再把结果和第5个元素计算:f(16, 9),结果为25;

由于没有更多的元素了,计算结束,返回结果25。

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

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

>>> from functools import reduce

>>> def fn(x, y):

...     return x * 10 + y

...

>>> reduce(fn, [1, 3, 5, 7, 9])

13579

附注:在Python3中,要使用reduce,得从functools中引入,加上from functools import reduce。

filter:

Python内建的filter()函数用于过滤序列。

和map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。

例如,在一个list中,删掉偶数,只保留奇数,可以这么写:

def is_odd(n):

return n % 2 == 1

list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))

# 结果: [1, 5, 9, 15]

把一个序列中的空字符串删掉,可以这么写:

def not_empty(s):

return s and s.strip()

list(filter(not_empty, ['A', '', 'B', None, 'C', '  ']))

# 结果: ['A', 'B', 'C']

可见用filter()这个高阶函数,关键在于正确实现一个“筛选”函数。

filter()函数返回的是一个新的list,不改变原list的结构。这个新的list中,如果判断条件成立(true)被判断的那个参数就被添加进来,如果判断条件不成立(false)就不添加。

注意到filter()函数返回的是一个Iterator,也就是一个惰性序列,所以要强迫filter()完成计算结果,需要用list()函数获得所有结果并返回list。

sorted:

排序也是在程序中经常用到的算法。无论使用冒泡排序还是快速排序,排序的核心是比较两个元素的大小。如果是数字,我们可以直接比较,但如果是字符串或者两个dict呢?直接比较数学上的大小是没有意义的,因此,比较的过程必须通过函数抽象出来。

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

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

[-21, -12, 5, 9, 36]

此外,sorted()函数也是一个高阶函数,它还可以接收一个key函数来实现自定义的排序,例如按绝对值大小排序:

>>> sorted([36, 5, -12, 9, -21], key=abs)

[5, 9, -12, -21, 36]

key指定的函数将作用于list的每一个元素上,并根据key函数返回的结果进行排序。对比原始的list和经过key=abs处理过的list:

list = [36, 5, -12, 9, -21]

keys = [36, 5,  12, 9,  21]

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

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

['Credit', 'Zoo', 'about', 'bob']

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

现在,我们提出排序应该忽略大小写,按照字母序排序。要实现这个算法,不必对现有代码大加改动,只要我们能用一个key函数把字符串映射为忽略大小写排序即可。忽略大小写来比较两个字符串,实际上就是先把字符串都变成大写(或者都变成小写),再比较。

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

>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)

['about', 'bob', 'Credit', 'Zoo']

要进行反向排序,不必改动key函数,可以传入第三个参数reverse=True:

>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)

['Zoo', 'Credit', 'bob', 'about']

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

小结

sorted()也是一个高阶函数。用sorted()排序的关键在于实现一个映射函数。

3.返回函数

函数作为返回值:

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

一个简单的实例:

def f():

print 'call f()...'

# 定义函数g:

def g():

print 'call g()...'

# 返回函数g:

return g

>>> x = f()   # 调用f()

call f()...

>>> x   # 变量x是f()返回的函数:

>>> x()   # x指向函数,因此可以调用

call g()...   # 调用x()就是执行g()函数定义的代码

请编写一个函数calc_prod(lst),它接收一个list,返回一个函数,返回函数可以计算参数的乘积。

# 第一种方法

def calc_prod(lst):

def lazy_prod():

def f(x, y):

return x * y

return reduce(f, lst, 1)

return lazy_prod

f = calc_prod([1, 2, 3, 4])

print f()

# 第二种方法

def calc_prod(lst):

def lazy_prod():

return reduce(lambda x,y:x*y,lst)

return lazy_prod

f = calc_prod([1, 2, 3, 4])

print f()

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

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

当我们调用la

zy_sum()时,返回的并不是求和结果,而是求和函数:

>>> f = lazy_sum(1, 3, 5, 7, 9)

>>> f

.sum at 0x101c6ed90>

调用函数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()的调用结果互不影响。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值