Haste makes waste

[Python-Liao-XX…]系列,系列根据廖雪峰的python3初级教程学习整理。

notebook

24. 函数式编程

``````abs(-10)
``````
``````输出：10
``````
``````abs
``````
``````输出：<function abs>
``````
``````x = abs
x(-10)
``````
``````输出：10
``````
``````x
``````
``````输出：<function abs>
``````

``````import math
abs = math.sqrt
``````
``````abs(4)
``````
``````输出：2.0
``````

25. 高阶函数

``````f = abs
``````
``````f(-1)
``````
``````输出：1
``````
``````f is abs
``````
``````输出：True
``````

25.1. 传入函数

``````def myfunc(a,b,f):
return f(a,b)
``````
``````def add(a,b):
return a + b
def plus(a,b):
return a*b
``````
``````myfunc(2,3,add)
``````
``````输出：5
``````
``````myfunc(3,4,plus)
``````
``````输出：12
``````

26. Map/Reduce

Map/Reduce都是高阶函数，能够在参数中接收其他函数名。

26.1 map

map函数接收两个参数，第一个是函数名，第二个是迭代器，比如 `f(x) = x**2, lista = [1,2,3,4,5,6] r = map(f,lista)`

``````def f(x):
return x**2

lista = [1,2,3,4,5,6]
r = map(f,lista)
list(r)
``````
``````输出：[1, 4, 9, 16, 25, 36]
``````

map()作为高阶函数，事实上它把运算规则抽象了，因此，我们不但可以计算简单的`f(x)=x**2` ，还可以计算任意复杂的函数，比如，把这个 list 所有数字转为字符串：

``````list(map(str,[1,2,3,4,5,6,7]))
``````
``````输出：['1', '2', '3', '4', '5', '6', '7']
``````

26.2 reduce

educe 把一个函数作用在一个序列[x1, x2, x3, …] 上，这个函数必须接收两个参数，reduce 把结果继续和序列的下一个元 素做累积计算，

``````from functools import reduce
``````
``````def add(a,b):
return a+b
``````
``````reduce(add,[1,2,3,4,5,6])
``````
``````输出：21
``````

1+2,和为3，3+3,和为6，6+4,和为10…,最后为21.

``````def f1(a,b):
return 10*a+b
reduce(f1,[1,2,3,4,5])
``````
``````输出：12345
``````
``````def char2Num(a):
return {"0":0,"1":1,"2":2,"3":3,"4":4,"5":5,"6":6,"7":7,"8":8,"9":9}[a]
``````
``````reduce(f1,map(char2Num,"15678945"))
``````
``````输出：15678945
``````

``````def str2int(s):
def f1(a,b):
return 10*a+b
def char2Num(a):
return {"0":0,"1":1,"2":2,"3":3,"4":4,"5":5,"6":6,"7":7,"8":8,"9":9}[a]
return reduce(f1,map(char2Num,s))
``````
``````str2int("1234563443")
``````
``````输出：1234563443
``````

``````def str2int2(s):
return reduce(lambda x, y: x * 10 + y, map(char2Num, s))
``````
``````str2int2("89765")
``````
``````输出：89765
``````

26.3 练习

• 将名字的第一个字母大写，后续全部小写
``````def normalize(name):
str_len = len(name)
return name[0].upper()+name[1:str_len].lower()
``````
``````L1 = ['adam', 'LISA', 'barT']
L2 = list(map(normalize, L1))
print(L2)
``````
``````['Adam', 'Lisa', 'Bart']
``````
• 求几个数的积
``````def prod(L):
return reduce(lambda a,b:a*b,L)
pass
``````
``````print("积:", prod([1, 2, 3, 4]))
``````
``````积: 24
``````
• 利用 map 和 reduce 编写一个 str2float 函数，把字符串’123.456’转换成 浮点数 123.456：
``````def str2float(s):
pass
``````
``````print('str2float(\'123.456\') =', str2float('123.456'))
``````
``````输出：str2float('123.456') = None
``````

27. filter

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

``````def is_odd(a):
return a%2== 1
``````
``````list(filter(is_odd,[1,2,3,4,4,6,7,7,8]))
``````
``````输出：[1, 3, 7, 7]
``````
``````def not_empty(s):
return s and s.strip()
``````

``````# and - 如果前面是True，则需要进一步计算，即将and 后面的返回；如果前面是false，则直接返回
print( True and "999")
print( False and "999")

# or - 如果前面是True，则直接返回前面，如果前面是false，则需要进一步计算，即将or后面的返回
print( True or "999")
print( False or "999")
``````
``````输出：999
False
True
999
``````
``````# 需要经过and 后面的strip处理
print(not_empty(' A'))
``````
``````输出：A
``````
``````list(filter(not_empty, [' A', '', 'B', None, 'C', '  ']))
``````
``````输出：[' A', 'B', 'C']
``````

27.1 用filter求素数

• 定义一个序列生成器，无限的
``````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 # 默认返回2这个素数
yield 4
yield 6
yield 8
it = _odd_iter()
while True:
n = next(it)
yield n
it = filter(_not_divisible(n), it) # 构造新序列
``````

``````# 打印 30 以内的素数:
for n in primes():
if n < 30:
print(n)
else:
break
``````
``````输出：2
4
6
8
3
5
7
11
13
17
19
23
29
``````

27.2 练习 - 打印回数(左→右 与 右→左 相同)

``````# and的用法，如果是true则返回and后面的，否则返回前面的false
def is_palindrome(n):
strN = str(n)
strN_right = ""
for i in range(len(strN)):
strN_right += strN[len(strN)-i-1]
return strN_right == strN and strN_right

# 测试:
output = filter(is_palindrome, range(1, 100))
print(list(output))
``````
``````输出：[1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99]
``````

28. Sorted 排序算法

``````sorted([1,2,3,4,-342,3,3,2,4])
``````
``````输出：[-342, 1, 2, 2, 3, 3, 3, 4, 4]
``````

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

``````sorted([1,2,3,4,-342,3,3,2,4],key=abs)
``````
``````输出：[1, 2, 2, 3, 3, 3, 4, 4, -342]
``````

``````sorted(['bob', 'about', 'Zoo', 'Credit'])
``````
``````输出：['Credit', 'Zoo', 'about', 'bob']
``````

``````sorted(['bob', 'about', 'Zoo', 'Credit'],key=str.lower)
``````
``````输出：['about', 'bob', 'Credit', 'Zoo']
``````

``````sorted(['bob', 'about', 'Zoo', 'Credit'],key=str.lower,reverse=True)
``````
``````输出：['Zoo', 'Credit', 'bob', 'about']
``````

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

29. 返回函数

``````def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum
``````

``````f1 = lazy_sum(3,4,5,6)
``````
``````f1
``````
``````输出：<function __main__.lazy_sum.<locals>.sum>
``````

``````f1()
``````
``````输出：18
``````

29.1 闭包

``````def count():
fs = []
print("count()执行")
for i in range(1, 4):
def f():
print("i执行:{}".format(i))
return i*i
fs.append(f)
return fs

f1, f2, f3 = count()
``````
``````count()执行
``````

fs，即函数count()的返回值，是一个包含指向函数f的变量的list。

``````print("result:{}".format(f1()))
print("result:{}".format(f2()))
print("result:{}".format(f3()))
``````
``````i执行:3
result:9
i执行:3
result:9
i执行:3
result:9
``````

``````def count():
print("count()执行")
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
print("i执行:{}".format(i))
fs.append(f(i)) # f(i)立刻被执行，因此 i 的当前值被传入 f()
return fs

f1, f2, f3 = count()
``````
``````count()执行
i执行:1
i执行:2
i执行:3
``````
``````print("result:{}".format(f1()))
print("result:{}".format(f2()))
print("result:{}".format(f3()))
``````
``````result:1
result:4
result:9
``````
• 关于变量的作用域
``````if 1 == 1:
name = "lzl"
print(name)

for i in range(10):
age = i

print(age)
``````
``````输出：lzl
9
``````

`这.....太诡异了吧，python中的函数块中的变量，在结束了函数块之后，居然还能访问，没有被释放！！！` 参考下下面的文章！！

1. python能够改变变量作用域的代码段是def、class、lamda.
2. if/elif/else、try/except/finally、for/while 并不能涉及变量作用域的更改，也就是说他们的代码块中的变量，在外部也是可以访问的
3. 变量搜索路径是：本地变量->全局变量
``````li = [lambda :x for x in range(10)]
``````

30. 匿名函数

• 传统的函数定义方式
``````def myfunc(x):
return x*x

list(map(myfunc,[1,2,3,4,5]))
``````
``````输出：[1, 4, 9, 16, 25]
``````
• 直接使用lambda表达式
``````list(map(lambda x: x*x,[1,2,3,4,5]))
``````
``````输出：[1, 4, 9, 16, 25]
``````

lambda表达式有个限制就是只能返回一个表达式，但是因为没有函数名，所以没有函数名冲突的问题存在，而且非常简洁。

``````f = lambda x:x*x
f
``````
``````输出：<function __main__.<lambda>>
``````
``````f(5)
``````
``````输出：25
``````

``````def build(x,y):
return lambda x,y:x*x + y*y
``````
``````f = build(0,0)
``````
``````f(4,7)
``````
``````输出：65
``````
``````
``````

31. 装饰器

PYTHON修饰器的函数式编程

``````import time
def log(func):
def wrapper(*args,**kw):
print("call %s():" %func.__name__)
return func(*args,**kw)

return wrapper

@log
def now():
print(time.time())
``````
``````now()
``````
``````输出:call now():
1509841556.247622
``````

``````import time
def log(func):
def wrapper(*args,**kw):
print("call %s():" %func.__name__)
return func(*args,**kw)

return wrapper

def now():
print(time.time())
``````
``````now = log(now)
now()
``````
``````输出:call now():
1509841556.267676
``````

32. 偏函数

Python中确实有非常多贴心的功能，不掌握这些用法就太亏了，下面的偏函数就是其中一种。

int(“12345”) , int函数其实也存在默认值，该函数等同于 int(“12345”,base=10),即默认值为转换为10进制。

``````int("12345",base=8)
``````
``````输出:5349
``````
``````int("12345",base=16)
``````
``````输出:74565
``````

``````def int16(s):
return int(s,base=16)

``````
``````int16("12345")
``````
``````输出:74565
``````
``````import functools
int2 = functools.partial(int, base=2)
``````
``````int2('1000000')
``````
``````输出:64
``````

max2(5, 6, 7) 相当于： args = (10, 5, 6, 7) max(*args) 结果为 10。

1. 提供代码重用性
2. 避免函数名和变量名冲突

34. 使用模块

python内置了很多模块，可以直接使用，以下面的代码为例，说明一个标准代码块的写法：

``````#!/usr/bin/env python3
# -*- coding: utf-8 -*-

' a test module '
__author__ = 'Michael Liao'

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()
``````
``````Too many arguments!
``````
1. 第1,2行注释是标准注释，第1行注释可以让这个hello.py文件直接在Unix/Linux/Mac上运行，第2行注释表示.py文件本身是UTF-8编码

2. 第 4 行是一个字符串，表示模块的文档注释，任何模块代码的第一个字 符串都被视为模块的文档注释。

3. 第 6 行使用__author__变量把作者写进去

4. `import sys`，导入 sys 模块后，我们就有了变量 sys 指向该模块，利用 sys 这个变量， 就可以访问 sys 模块的所有功能。

5. sys 模块有一个 argv 变量，用 list 存储了命令行的所有参数。argv 至少 有一个元素，因为第一个参数永远是该.py 文件的名称，比如运行 python3 hello.py Michael 获得的 sys.argv 就是[‘hello.py’, ‘Michael]。

6. `if __name__=='__main__': `当我们在命令行运行 hello 模块文件时，Python解释器把一个特殊变量 name__置为__main，而如果在其他地方导入该 hello 模块时，if 判断将失败，即不会执行if内的代码。 因此，这种if测试可以让一个模块通过命令行运行时执行一 些额外的代码，最常见的就是运行测试。

1. 作用域

1. 正常的函数名和变量名都是public，可以直接引用
2. `__xxx__`这样的变量是特殊变量，可以被直接引用，但是有特殊用途，比如上面的`__author``__name__`，hello模块定义的文档 注释也可以用特殊变量__doc__访问， 我们自己的变量一般不要用这种变 量名；
3. 类似`_xxx``__xxx` 这样的函数或变量就是非公开的（private），不应该被 直接引用，比如_abc，__abc 等；

99. 【重要】下面的and和or

``````'123' or '456'  -> '123'  # bool('123') がTrueなので、 左辺値'123'をリターン
'123' or ''     -> '123'  # bool('123') がTrueなので、 左辺値'123'をリターン
''    or '456'  -> '456'  # bool('')    がFalseなので、右辺値'456'をリターン
''    or ''     -> ''     # bool('')    がFalseなので、右辺値''   をリターン
'123' and '456' -> '456'  # bool('123') がTrueなので、 右辺値'456'をリターン
'123' and ''    -> ''     # bool('123') がTrueなので、 右辺値''   をリターン
''    and '456' -> ''     # bool('')    がFalseなので、左辺値''   をリターン
''    and ''    -> ''     # bool('')    がFalseなので、左辺値''   をリターン
``````