python入门之函数式编程

Python是一门多范式编程语言,它在很大程度上支持函数式编程。那么什么是函数式编程呢?我的理解是函数的输入和输出都可以是函数的编程方式。当然我理解可能是错的,或者是片面的,本篇就通过搜集一些网上关于python函数式编程的资料,以期能更深入理解下函数式编程。

函数式编程

先来段引用,出自百度百科

函数式编程是种编程方式,它将电脑运算视为函数的计算。函数编程语言最重要的基础是λ演算(lambda calculus),而且λ演算的函数可以接受函数当作输入(参数)和输出(返回值)。
和指令式编程相比,函数式编程强调函数的计算比指令的执行重要。
和过程化编程相比,函数式编程里函数的计算可随时调用。

函数编程具有如下五个鲜明特点:

  1. 函数是一等公民:函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。
  2. 只用表达式不用语句:表达式(expression)是一个单纯的运算过程,总是有返回值;语句(statement)是执行某种操作,没有返回值。函数式编程要求,只使用表达式,不使用语句。也就是说,每一步都是单纯的运算,而且都有返回值。
  3. 没有副作用:副作用(side effect)指的是函数内部与外部互动(最典型的情况,就是修改全局变量的值),产生运算以外的其他结果。函数式编程强调没有”副作用”,意味着函数要保持独立,所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。
  4. 不修改状态:上一点已经提到,函数式编程只是返回新的值,不修改系统变量。因此,不修改变量,也是它的一个重要特点。
  5. 引用透明性:函数程序通常还加强引用透明性,即如果提供同样的输入,那么函数总是返回同样的结果。就是说,表达式的值不依赖于可以改变值的全局状态。

函数式变成具有以下优点:

  1. 代码简洁,开发快速
  2. 接近自然语言,易于理解
  3. 更方便的代码管理
  4. 易于”并发编程”
  5. 代码的热升级

Python函数式编程

高阶函数

输入(参数)可以为函数的函数是高阶函数。例如:

1
2
3
def add(a, b, f):
return f(a) + f(b)
add(-1, -2, abs)

上面的add函数为自定义的高阶函数。python提供了一些高阶函数,如:map、reduce、filter、sorted等,下面介绍之。

map

天下大势,分久必合,合久必分,这里的map就是分,举例:求1到100每个整数的平方。

我们传统的思维应该是这样的:

1
2
3
4
5
l = range(1, 101)
l1 = []
for i in l:
l1.append(i*i)
print l1

用map应该是这样的:

1
2
3
4
def sq(i)
return i*i
l2 = map(sq, l)
print l2

其思想就是将之前依次每个求平方变为分别求平方,至于map是怎么分别的我就不管了。

或有人说,就这个还用这么费劲,我有更简单的:

1
2
l3 = [i*i for i in l]
print l3

这个代码量上是简单了,但是本质还是上面的第一种,这只是python为简化此类操作提供的列表生成式。

那接着举例子,看你还有什么简单写法,求1到100的整数之和。这个最简单的方法就是背过了,呵呵5050,我们来看用reduce的实现。

reduce

map应了分,reduce就应了合。就上面例子我们看reduce怎么做:

1
2
3
def add(a, b):
return a + b
sum = reduce(add, l)

好了,这就是reduce的使用例子了。正常思维的写法就不上代码了,至于有没有像上面列表生成式那种简单的写法我是不知道。

filter

filter就是过滤器了,从已有列表中过滤出满足条件的。接着上例子列出1到100的所有偶数,看filter是怎么做的:

1
2
3
def even(i):
return i%2 == 0
l4 = filter(event, l)

就这么简单,当然列表生成式也可以很简单。

sorted

下面我们来把1到100的整数顺序打乱,使用sorted进行排序。

1
2
3
4
import random
l = range(1,101)
random.shuffle(l)
sorted(l)

这样会从小到大排序,当然原来的列表顺序是不变的,会输出新列表。

sorted(l, reverse=True)这样就可以从大到小排序。下面看怎么自定义排序方式:

1
2
3
4
5
6
7
8
def cmp(a, b):
if a > b:
return -1
elif a < b:
return 1
else:
return 0
sorted(l, cmp=cmp)

要想从大到小排序,就在第一个参数大于第二个参数时返回负数,反之就那样。我来个更兼的写法,顺便引出匿名函数

1
sorted(l, cmp=lambda x,y:y-x)

匿名函数

先来个例子lambda x,y:x+y,这里lambda表示匿名函数,空格后冒号前表示匿名函数的参数,冒号后只能有一个表达式,不能有return,表达式的运算结果表示函数的返回值。这样例子的匿名函数就与下面等效:

1
2
def func(x, y):
return x + y

匿名函数也可以没有参数,也可以作为返回值返回,例如:

1
2
3
4
def build(x, y):
return lambda: x*x + y*y
f = build(3,4)
c = f() #这时c是25

闭包

内层函数引用了外层函数的变量,然后返回内层函数的情况,称为闭包(closure)。抄个网上的例子:

1
2
3
4
5
6
7
8
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()

可以试一下什么结果。

闭包的特点是返回的函数还引用了外层函数的局部变量,所以,要正确使用闭包,就要确保引用的局部变量在函数返回后不能变。

装饰器

python的装饰器(decorator)本质是一个高阶函数,它接收被装饰的函数作为参数,返回一个新瀚抒,这个新函数内部执行了被装饰的函数。下面来个装饰器例子:

1
2
3
4
5
6
def log(func):
def wrapper(*args, **kw):
print 'before call %s()...'%func.__name__
func(*args,**kw)
print 'after call %s()...'%func.__name__
return wrapper

上面定义了一个装饰器log,下面定义两个函数:

1
2
3
4
def hello():
print 'hello,world.'
def add(a,b):
print a+b

那么,如何用装饰器来装饰它们呢,请看:

1
2
3
4
f1 = log(hello)
f2 = log(add)
f1()
f2(3,4)

但是,正常是没有这样用的,一般这样就好了:

1
2
3
4
5
6
7
8
9
@log
def hello():
print 'hello,world.'
@log
def add(a,b):
print a+b

hello()
add(3,4)

这下就方便多了。

类装饰器

类装饰器这个名字是我自己取的,应该是不对的。反正我想表达的意思是在定义一个类的方法时经常会用到的@property、@staticmethod、@classmethod等。直接看例子吧。

@property

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Square:
def __init__(self,sideLen):
self.sideLen = sideLen
@property
def perimeter(self):
return self.sideLen*4
@property
def area(self):
return self.sideLen*self.sideLen
#测试
s = Square(5)
print s.sideLen
print s.area
print s.perimeter

@staticmethod

1
2
3
4
5
6
7
8
class Foo:
@staticmethod
def spam(x,y,z):
print x,y,z
#测试
Foo.spam(1,2,3)
f = Foo()
f.spam(3,2,1)

@classmethod

1
2
3
4
5
6
7
8
9
10
11
class A:
@classmethod
def test(cls):
print cls.__name__
class B:
pass
#测试
A.test()
B.test()
b=B()
b.test()

说明就不说了,我抄的这篇

总结

我看https://www.liaoxuefeng.com关于python函数式编程还有偏函数,先不记录了。