#不知不觉已经学到第21天了,在这期间自己确实从一个什么都不会的菜鸟变成了一个依然什么都不会的大菜鸟,学了这么多的东西,却感觉用不到任何地方,与其这样说,不如比喻成:我想要造机器,现在才学会了认识螺丝,螺母,完全还达不到组装一个胳膊这么简单而又基础的步骤,我很佩服那些说在一个月或者两个月就能学会python的人,他们应该都是有着极高天赋的学霸。真希望自己也能像他们一样!
#在学习期间遇到了很多问题其实到现在依然还有些朦胧,感觉会,又感觉不会,知识在学习第一遍的时候总是会给你一种错觉,那就是这一课很简单嘛!实际上过了两天,甚至连表达式都不会写了。
#今天看到交流群里有一个人说的学习递归的时候很懵,并且作业1+2+3...这种题用递归也做不出来,当时看到后第一反应是这么简单的题怎么能看不懂呢?然后自己就默默的打开了cmd,就在准备打下第一个字符的时候,我突然发现原来自己也忘得一干二净了,惭愧!甚至在用for循环完成表达式的时候都是写了好几遍,通过这次,我发现原来自己对于基础的掌握是如此的浅显,甚至只要学习了新的东西,就会把旧的忘得一干二净!
#从20天开始,感觉后面的东西就变难了,其实到了高级特性这里就像是一个进阶,从基础单一的函数变成了各种组合,而后面的函数式编程,模块以及各种编程应该会更加具有挑战性,更加容易让人放弃。
第21天:高阶函数-map/reduce
高阶函数:一个函数中的参数包含另外一个函数就是高阶函数
#高阶函数在理解方便其实还是很简单的,通过网站给出的几个例子,我们就能明白上述那段话的意思
1.变量可以被指向为函数:
abs:将所有整数变为绝对值
>>> abs(-10)
10
#↑这是一个函数调用,调用的abs函数,来将-10变为绝对值。
>>> abs
#↑这是一个函数本身,需要填写参数才能正常调用
>>>x = abs(-10)
>>>x
10
#↑X为变量,将调用的函数X绝对值赋予给它
#如果将变量赋予函数呢?
>>> x = abs
>>>x(-10)
10
#x现在就变为了abs函数,并且可以直接调用它,也就是变量可以被指向为函数!
2.函数名也可以是变量
#通过上面几个例子我们已经知道变量和函数的定义了,我们直接将函数名进行赋值
>>>abs = 10
>>>abs
10
#通过上面来看,我们就发现了abs不再是个函数,而是一个正常的变量,因为如果他是函数应该会出现
#不过实际上代码千万不能这么写,否则会出现一些混乱。此外,只是在当前修改函数的话,也不会在所有模块生效,如果想要生效,则用import builtins; builtins.abs = 10公式(我觉得应该是用不到)
3.传入函数:既然变量可以指向函数,函数也可以变为变量,那么将他们用在一起,就是高阶函数,通过以下简单的公式就可以更直白的了解这个机制:
>>> def add(x,y,z):
... return z(x) + z(y)
...
>>> add(-5,6,abs)
11
#其实上述公式很容易就能了解,不过为了防止后面自己再回看会发蒙,还是将公式拆解:
x = -5
y = 6
z = abs
#此时,z就已经被认作为abs函数了,下面的计算就简单了:
return abs(-5) + abs(6)
-5 + 6 = 11
-------------------------------------------------------------------------------------------------------------------------------------
#好了,简单的东西就梳理完了,然后就是自己看了几遍也不是很明白的map/reduce(可能后面其他的函数也是一样懵)#
#做个记号,以后回来时再看一看这里会不会有新的感悟#
map函数:需要传入两个参数,一个是函数,一个是Iterable,接下来会将参数逐一带入到Iterable中,变为Iterator
#Iterable在前两天刚有学到,就是可迭代的:list、tuple、dict、set、str、generator这几种
#map函数做一个简单的例子:先定义一个平方函数square,接下来使用map函数代入:一边放函数square,一边放iterable
>>> def square(x):
... return x * x
...
>>> r = map(square,[1,2,3,4,5])
>>> list(r)
[1, 4, 9, 16, 25]
#为什么要赋予变量r呢?因为在定义里面也说到了,map得出的结果最后是Iterator,同generator一样,得出的是一个算法,所以需要将它赋予变量r,再使用list遍历。否则就会变成一堆算法。
#当然除了map函数外,我们还可以使用循环来得出结果:
>>> def square(x):
... return x * x
...
>>> l = []
>>> for n in range(1,6):
... l.append(f(n))
... print(l)
[1]
[1, 4]
[1, 4, 9]
[1, 4, 9, 16]
[1, 4, 9, 16, 25]
#还可以用更简单的列表生成式来计算:
>>> [x * x for x in range(1,6)]
[1, 4, 9, 16, 25]
#虽然我觉得列表生成式更加简单一些,不过从网站的解释来看map更能一眼就看出square(x)代入到数列中!(就当是这样吧,毕竟现在还是不太懂)
#map函数作为高阶函数,将流程变得更加简化,除了代入square这样的计算函数,还可以直接一行代码将list转换为str
>>> list(map(str,[1,2,3,4,5,6,7]))
['1', '2', '3', '4', '5', '6', '7']
reduce:这个函数也是需要导入两个参数:一边是函数,另一边是序列,另外,函数需要带有两个参数,然后会将这个函数的结果和序列的下一个元素做累积:
#使用reduce函数必须先用from导入才可以:from functools import reduce
#上述的导入和之前学到的from collection import倒是一样。
#如果看起来比较懵,可以直接举一个简单的例子:序列求和
>>> from functools import reduce
>>> def add(x,y):
... return x + y
>>> reduce(add,[1,2,3,4,5,6])
21
#其实算法很简单,拆解开来就是,将X+Y代入到数列中:
x = 1 , y =2
1+2=3+3=6+4=10+5=15+6=21
#用公式写法更加清晰:add(add(add(1+2)+3)+4)
#当然,求和有更好的方法,使用上面的例子并不合适,我们再举一个相似的,也是更加了解计算机制:将序列变为一个整数
>>> from functools import reduce
>>> def fn(x,y):
... return x * 10 + y
...
>>> reduce(fn,[1,3,5,7,9])
13579
#我这智商是没有办法想出这么好的计算方法的,但实际上函数一旦列出,就很容易推导出计算方法:
1*10+3=13*10+5=135*10+7=1357*10+9=13579
#通过上述可以看出,开始的前两位是被X,Y占据的,所以在第二次代入的时候是直接从第三位开始,然后才是顺序计算。
#下面举一个具有实际用途的例子:将字符串(str)转换为int的函数,结合map与reduce
>>> from functools import reduce
>>> def fn(x,y):
... return x * 10 + y
...
>>> def oh(x):
... djs = {'0':0,'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9}
... return djs[x]
...
>>> reduce(fn,map(oh,'13579'))
13579
#给自己拆解一下上述这串代码的步骤:
1.首先还是定义函数fn,将算法进行列举,便于后面代入reduce转换
2.其次列出dict(与set意义相似,只是dict需要key与value相对应)
3.djs[x]其实就是将x对应的字符代入找寻set中对应的value
4.使用map函数将Oh函数代入到str中,因为str是iterable,所以也是可以逐一找到相对应的value,生成list
5.再使用reduce将fn函数带入到list中计算,得出最终结果。
#最后还可以将上述函数再进行整理,最终编程一个函数:
>>> from functools import reduce
>>> digits = {'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9}
>>> def str2int(s):
... def fn(x,y):
... return x * 10 + y
... def char2num(s):
... return digits[s]
... return reduce(map,(char2num,s))
#最后还有一套简化方案,不过里面涉及到了一个不认识的函数,说后面会讲,所以暂时只复制过来
>>> from functools import reduce
>>> DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
>>> def char2num(s):
... return DIGITS[s]
... def str2int(s):
... return reduce(lambda x, y: x * 10 + y, map(char2num, s))
今日作业总共有三个:
一、利用map()函数,把用户输入的不规范的英文名字,变为首字母大写,其他小写的规范名字。输入:['adam', 'LISA', 'barT'],输出:['Adam', 'Lisa', 'Bart']:
def normalize(name):
# 测试:
L1 = ['adam', 'LISA', 'barT']
L2 = list(map(normalize, L1))
print(L2)
二、Python提供的sum()函数可以接受一个list并求和,请编写一个prod()函数,可以接受一个list并利用reduce()求积:
# -*- coding: utf-8 -*-
from functools import reduce
def prod(L):
print('3 * 5 * 7 * 9 =', prod([3, 5, 7, 9]))
if prod([3, 5, 7, 9]) == 945:
print('测试成功!')
else:
print('测试失败!')
三、利用map和reduce编写一个str2float函数,把字符串'123.456'转换成浮点数123.456:
from functools import reduce
def str2float(s):
print('str2float(\'123.456\') =', str2float('123.456'))
if abs(str2float('123.456') - 123.456) < 0.00001:
print('测试成功!')
else:
print('测试失败!')
作业一思路
upper()为大写转换,lower()为小写转换,公式中可以检测首个字母将其变为大写,其后变为小写,所以公式可以写作:
>>> def normalize(name):
... return name[0].upper() + name[1:].lower()
在写完作业之后,发现原来还有title这个函数 - -!,可以直接将第一位变为大写,后续为小写的规则(真的是见识少)!
>>> def normalize(name):
... return name.title()
作业二思路:
1、reduce函数就是将一个函数作用在一个序列中,所以想要求一个序列的积,需要满足两个条件,一个是乘积函数,一个是序列
2、现在只有一个prod,所以将它变为一个序列,然后再创建一个函数得出乘积,最后再用reduce进行计算得出结果,只不过最后是只导入一个prod,所以如果写两个def并且都有return的话,就会出现问题。
>>> def prod(L):
... def prod_air(x,y):
... return x * y
... return reduce(prod_air,L)
#关于作业二,我看到还有好多同学再用另一个函数,就是上面说的lambda这个函数,等待后面学到后再回来用吧。
作业三思路,这里就先不写了