文章目录
函数式编程(Functional Programming)
函数式编程:思想更接近于数学计算,抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量。
特点:允许把函数本身作为参数传入另一个函数,还允许返回一个函数。
Python 允许使用变量,因此,Python 不是纯函数式编程语言。
一、高阶函数(Higher-order function)
函数本身也可以赋值给变量,即:变量可以指向函数。例如:函数名。
f = abs
print(f(-10))
输出结果
10
高阶函数:一个函数就可以接收另一个函数作为参数。(既然变量可以指向函数,函数的参数能接收变量)
def add(x, y, f):
return f(x) + f(y)
print(add(-5, 6, abs))
输出结果
11
二、map/reduce
Python 中内建了 map() 和 reduce() 函数。
1、map() 函数
map()函数:接收两个参数,一个是函数,一个是 Iterable。map 将传入的函数依次作用到序列的每个元素,并把结果作为新的 Iterator 返回。
def f(x):
return x * x
r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
print(list(r))
## 把这个 list 所有数字转为字符串
print(list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])))
输出结果
[1, 4, 9, 16, 25, 36, 49, 64, 81]
['1', '2', '3', '4', '5', '6', '7', '8', '9']
2、reduce() 函数
reduce()函数:把一个函数作用在一个序列 [x1, x2, x3, ...]
上,这个函数必须接收两个参数, reduce 把结果继续和序列的下一个元素做累积计算,其效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
把序列[1, 3, 5, 7, 9]变换成整数 13579 的 reduce 实现:
from functools import reduce
def fn(x, y):
return x * 10 + y
print(reduce(fn, [1, 3, 5, 7, 9]))
输出结果
13579
str 类型 ==》int 类型
from functools import reduce
def str2int(s):
def fn(x, y):
return x * 10 + y
def char2num(s):
return {'0':0,'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9}[s]
return reduce(fn,map(char2num,s))
# 13579
print(str2int('13579'))
利用 lambda 函数进一步简化:
def char2num(s):
return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
def str2int(s):
return reduce(lambda x,y:10 * x + y, map(char2num, s))
str ==》float
def str2float(s):
def char2num(s):
return {'0':0,'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9}[s]
def fnMuti(x, y):
return 10 * x + y
def fnDivid(x, y):
return x / 10 + y
dotIndex = s.index('.')
return reduce(fnMuti, map(char2num, s[:dotIndex])) + reduce(fnDivid, list(map(char2num, s[dotIndex + 1:]))[::-1])/10
print('str2float(\'123.456\') =', str2float('123.456'))
输出结果
str2float('123.456') = 123.456
三、filter
filter() 函数用于过滤序列。接收两个参数:一个函数和一个序列,filter() 把传入的函数依次作用于每个元素,然后根据返回值是 True ,则保留,为 False 则丢弃该元素。
def is_odd(n):
return n % 2 == 1
print(list(filter(is_odd, [1,2,4,5,6,9,10,15])))
# 结果: [1, 5, 9, 15]
==》filter() 函数返回结果是 Iterator,是一个惰性序列,需要使用 list() 函数获取所有的结果并返回 list。
1、用 filter 求素数
实现方法:埃氏筛法。列出从 2 开始的所有自然数,构造一个序列:取序列第一个数并筛掉其的倍数,依次类推…
# 生成器,且是一个无限序列
def _odd_iter():
n = 1
while True:
n = n + 2
yield n
# 过滤函数
def _not_divisible(n):
return lambda x : x % n > 0
# 用于不断返回素数的生成器
def primes():
yield 2
it = _odd_iter() # 初始序列
while True:
n = next(it)
yield n
it = filter(_not_divisible(n), it) # 构造新的序列
# 设置一个退出循环的条件
for n in primes():
if n < 1000:
print(n)
else:
break
四、sorted
通常规定,对于两个元素 x 和 y,如果认为 x < y,则返回-1,如果认为 x == y,则返回 0,如果认为 x > y,则返回 1
1、数字
## 原始
print(sorted([36, 5, -12, 9, -21]))
## sorted()函数也是一个高阶函数,它还可以接收一个 key 函数来实现自定义的排序。
## key 指定的函数将作用于 list 的每一个元素上,并根据 key 函数返回的结果进行排序。
print(sorted([36, 5, -12, 9, -21], key=abs))
输出结果
[-21, -12, 5, 9, 36]
[5, 9, -12, -21, 36]
2、字符串
## 默认情况下,对字符串排序,是按照 ASCII 的大小比较的
print(sorted(['bob', 'about', 'Zoo', 'Credit']))
## 可实现忽略大小写的排序
print(sorted(['bob', 'about', 'Zoo', 'Credit'], key = str.lower))
## reverse 为 true 表示反向排序
print(sorted(['bob', 'about', 'Zoo', 'Credit'], key = str.lower, reverse = True))
输出结果
['Credit', 'Zoo', 'about', 'bob']
['about', 'bob', 'Credit', 'Zoo']
['Zoo', 'Credit', 'bob', 'about']
五、返回函数
1、函数作为返回值
例如:返回求和的函数
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum
f1 = lazy_sum(1,3,5,7,9)
f1
f1() # 函数需要调用才能执行
# 调用 lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数
f2 = lazy_sum(1,3,5,7,9)
f1 == f2
输出结果
<function __main__.sum>
25
False
分析:在函数 lazy_sum 中又定义了函数 sum,并且,内部函数 sum 可以引用外部函数 lazy_sum 的参数和局部变量,当 lazy_sum 返回函数 sum 时,相关参数和变量都保存在返回的函数中,这种称为“闭包( Closure) ”的程序结构拥有极大的威力。
2、闭包
返回函数不要引用任何循环变量, 或者后续会发生变化的变量。
3、匿名函数
4、装饰器
装饰器”( Decorator):在代码运行期间动态增加功能的方式。
5、偏函数
模块
模块:Python中一个.py文件就是一个模块(Module)
==》提高了代码的可维护性;复用性;避免函数名和变量名冲突。
包(Package):按目录来组织模块的方法。例如:a.py(a模块) 与 b.py(b模块)与其他模块冲突,则选择一个顶层包名,例如mycompany,则 a 模块变成 mycompany.a、b模块变成 mycompany.b
==》每一个包目录下面都会有一个__init__.py 的文件,这个文件是必须存在的,用于标识包。
1、使用模块
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
'a test module' ## 表示模块的文档注释,任何模块代码的第一个字符串都被视为模块的文档注释
__author__ = '盛夏光年'
import sys
def test():
args = sys.argv
if len(args)==1:
print("Hello world!")
elif len(args) == 2:
print('Hello, %s!' % args[1])
else:
print('Too many arguments!')
if __name__ == '__main__':
test()
2、作用域
- 正常的函数和变量名是公开的( public),可以被直接引用,比如: abc,x123, PI 等;
- 类似__xxx__这样的变量是特殊变量,可以被直接引用,但是有特殊用途;
- 类似_xxx 和__xxx 这样的函数或变量就是非公开的( private),不应该被直接引用,比如_abc, __abc 等;
注意: Python 并没有一种方法可以完全限制访问 private 函数或变量
3、第三方库
eg: Pillow、numpy、Jinja2
查看搜索目录:
import sys
print(sys.path)
添加搜索目录——方法1:
import sys
sys.path.append('/Users/michael/my_py_scripts')
==》这种方法是在运行时修改,运行结束后失效。
添加搜索目录——方法2:
设置环境变量 PYTHONPATH