python3手册的目录
Python与函数式编程
Python的函数式不是特别有名, 但是我们还是可以做一些函数式编程的.
Python目前没有内置的不可变集合类库像Clojure设计的那样优秀. Clojure的不可变的集合不仅在修改的时候对内存的利用率很高, 并且很适合多线程直接传递数据. 避免加锁.
下面介绍的函数式编程使用的是Python3内建的集合类, 在多线程之间传递时, 我们要做好加锁工作. 或者做深度拷贝. 或者我们将对共享资源的访问给一个线程来操作, 其他线程通过queue
模块来给操作资源的那个线程发送命令. queue
的使用示例: Python3多线程编程
下面的方式不叫函数式编程
下面的方式中, 函数increment
使用了外部的变量a
, 因此有副作用
a = 0
def increment():
global a
a += 1
下面的方式是函数式编程
def increment(a):
return a + 1
什么是函数式编程
函数式编程是一种编程模式, 它避免使用可变数据类型, 避免状态的改变. 流行的函数式语言有: Lisp, Haskell, Clojure.
函数式语言是描述型语言的子集. 描述型语言区别于命令型语言(c/c++,go,java)
函数式语言的特点
- 避免用全局状态的来解决问题
- 使用不可变数据类型(最好设计成内存使用率高的方式)
- 在语言中, 函数是一等公民(像java就不是)
- 使用高阶函数
- 使用递归函数
前两条主要是用来消除副作用. 如果状态的变更不依赖于输入函数的输入, 那么查找bug会是很麻烦是事情.
状态的表示和转移
在函数式编程中, 函数将输入转变成输出, 不产生和维护中间状态.
我们来看看下面的例子:
def square(x):
return x*x
input = [1, 2, 3, 4]
output = []
for v in input:
output.append(square(v))
def square(x):
return x*x
input = [1, 2, 3, 4]
output = map(square, input)
以上两段代码完成的任务是一样的. 但是我们可以看到, 第二种没有中间状态. 第一种在执行的时候, output
变量一直在改变, 并且是可以被其他线程访问到的. 但是第二种不改变input
也不产生中间状态, 只有计算完后, 才输出output
变量.
一等公民的函数, 和高阶函数
在Python我们可以直接定义一个函数, 然后使用它, 不像Java, 必须有一个对象或者类的前提下才能使用函数.
def greet(name):
print("Hello %s!" % name)
say_hello = greet
say_hello('World')
# Hello World!
高阶函数, 就是把函数作为参数的函数, 或者返回一个函数的函数.
比如Python中的内建函数map()
reduce() 以及 filter()
reduce
函数将一个序列的数据, 归结为一个输出.
from functools import reduce
sum = reduce(lambda a, x: a + x, [0, 1, 2, 3, 4])
filter
函数将一个序列中的每个元素进行筛选, 并且返回一个新的序列
def is_odd(n):
return n%2 == 1
print(is_odd(2))
# False
print(is_odd(3))
# True
values = [1, 2, 3, 4, 5, 6]
result = filter(is_odd, values)
print(list(result))
# [1, 3, 5]
生成器以及懒赋值
在Python中, 生成器Generator Functions和生成器表达式Generator Expressions他们的行为都跟迭代器一样. 好处就是不必预先将整个数据序列计算出来并且放入内存. 只是需要再使用的时候才去计算, 用多少计算多少call-by-need strategy
.
下面的图为 generator iterator iterable container 之间的关系.
代码示例
下面的例子默认:
from funcs import seq
使用了链式调用, 类似 Kotlin 的风格
例子1:
对一个序列进行循环迭代, 然后只取前10个元素,
最后将取到的10个元素保存到list中.
这里不到最后的list()调用, 是不会将整个结果生
成到内存中的.
这样对于需要大量的数据进行迭代的应用来说,
效率很高, 对内存很友好.
a =seq([1, 22, 3, 45]).cycle().head(10).list()
print(a)
输出: [1, 22, 3, 45, 1, 22, 3, 45, 1, 22]
例子2:
将多个序列合并
a = seq([1, 22, 3, 45]).chain([1, 2, 4, 5, 2], [5, 65, 6, 90])
print(list(a))
输出: [1, 22, 3, 45, 1, 2, 4, 5, 2, 5, 65, 6, 90]
例子3:
对序列进行求平方, 再过滤出大于5的列表, 最后对列表进行求和,
求和的初始值为0. 也就是说, sum的初始值为0,
x从filter()产出的列表的第一个元素开始遍历.
a = seq([1, 2, 3, 4, 5]).map(lambda x: x ** 2).filter(lambda x: x > 5).reduce(lambda sum, x: sum + x, 0)
print(a)
输出: 50
funcs.py 模块
import collections
from functools import reduce
from array import array
import itertools
class seq:
def last_index(self):
t = list, array, str, tuple, bytes
if isinstance(self.data, t):
return len(self.data) - 1
else:
raise TypeError(f"{type(self.data)} is not instance of {t}")
def size(self):
t = collections.Sized,
if isinstance(self.data, t):
return len(self.data)
raise TypeError(f"{type(self.data)} is not instance of {t}")
def __init__(self, _s):
t = collections.Iterable,
if isinstance(_s, t):
self.data = _s
else:
raise TypeError(f"{type(_s)} is not instance of {t}")
def __iter__(self):
return iter(self.data)
def map(self, f):
return seq(map(f, self.data))
def filter(self, f):
return seq(filter(f, self.data))
def reduce(self, f, init=None):
return reduce(f, self.data, init)
def zip(self, *iter):
return seq(zip(self.data, *iter))
def cycle(self):
return seq(itertools.cycle(self.data))
def chain(self, *iter):
return seq(itertools.chain(self.data, *iter))
def list(self):
return list(self)
def head(self, n=10):
def gen():
nonlocal n
for d in self:
if n <= 0:
break
n -= 1
yield d
return seq(gen())
参考:
Functional Programming in Python
A practical introduction to functional programming