将以下内容作为第二部分。内容中涉及的程序均来源于廖老师的Python教程(网址为https://www.liaoxuefeng.com/wiki/1016959663602400/)。
四、高级特性
引入高级特性,可以缩减代码量。
1. 切片
L[0:3]取列表中的前3个元素,等价于L[:3]。
L[-2:]获取最后的2个元素。L[-2:-1]获取倒数第2个元素。
L[:10:2]从第1个元素开始每隔2个开始获取元素,直到第10个元素。
L[::5]以5为步长来获取列表中所有元素。
L[:]获取列表中的所有元素。
对于tuple和str,也可以用上面的切片方式获得元素。
去掉字符串首尾的空字符:
def trim(s):
while s[:1]==’’
s=s[1:]
while s[-1:]==’’
s=s[:-1]
return s
2. 迭代
for ... in...
字典的例子:
>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> for key in d:
... print(key)
获得的是key值。迭代出的结果并不一定是按照a b c的顺序输出的。如果迭代value的话,应该是用for value in d.values()。迭代key和value的话,应该是用for k, v in d.items()。
判断对象是否是可迭代对象的方式:Iterable。
>>> from collections import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代
True
>>> isinstance([1,2,3], Iterable) # list是否可迭代
True
>>> isinstance(123, Iterable) # 整数是否可迭代
False
若想变为索引-元素的形式,应该用enumerate,如:
>>> for i, value in enumerate(['A', 'B', 'C']):
... print(i, value)
0 A
1 B
2 C
3. 列表生成式
>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]
isinstance(xxx, str) 用isinstance判断变量xxx是否是字符串str类型。如果是的话,则结果为True;否则结果是False。
4. 生成器
从节省存储空间的角度出发,引入生成器。如下所示,即将[ ]换为( )即可。
>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10)) #即获得生成器
>>> g
<generator object <genexpr> at 0x1022ef630>
依次用next(g)可以依次获取g中的元素。为了更好地获取g中的元素,采用for循环的方式来代替next,有
>>> g = (x * x for x in range(10))
>>> for n in g:
... print(n)
0
1
4
9
16
25
36
49
64
81
定义generator,有
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b #把print(b)替换为yield b,变为generator
a, b = b, a + b
n = n + 1
return 'done'
因而,可以得到
>>> f = fib(6)
>>> f
<generator object fib at 0x104feaaa0>
遇到yield返回。再执行时,从开始yield后执行。while n < max是设置的终止循环的条件。除此之外,可以采用for或者while来实现next()的作用。
>>> for n in fib(6):
... print(n)
1
1
2
3
5
8
5. 迭代器
和之前的一样,可以用isinstance(xxx, Iterable)来判断对象xxx是否是Iterable对象,即可迭代对象。
可以用next()函数调用并不断返回下一个值的对象称为迭代器Iterator。
>>> isinstance([], Iterator)
False
可以用isinstance判断对象是否是迭代器。
注:生成器都是迭代器。可以用iter()将可迭代对象变为迭代器。不是迭代器的可迭代对象有list, str, dict等。原因在于迭代器会不断地返回下一个元素,没有指定数据的数量大小。
总结:
- 可以用于for循环的对象都是Iterable。
- 可以用作next()的对象都是Iterator。
- list,dict,str是Iterable而不是Iterator,可以通过iter()转变为Iterator。
- Python的for循环本质上是通过next()来实现的(iter()将Iterable转变为Iterator,再循环使用next())。
五、函数式编程
函数式编程:抽象程度高。
允许将函数作为参数传入另一个函数,也支持返回一个函数。
纯函数式编程:函数中没有变量。只要给定输入,就可以确定输出,这样的函数式是没有副作用的。Python是允许在函数中使用变量的,因而不是纯函数式编程。
1. 高阶函数
>>> f = abs
>>> f(-10) #和abs(-10)是等效的
10
变量f指向函数abs,也可以通过变量来调用函数。
可以认为函数名abs也是一个变量,它指向一个可以实现绝对值计算的函数。当把该变量指向其它对象时,会出现错误,如下所示:
>>> abs = 10
>>> abs(-10)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
函数可以作为参数传入另一个函数中,支持将函数作为参数来传入的函数被称为高阶函数。
(1)map/reduce
map:输入两个参数(函数和Iterable),输出Iterator。
reduce:将内部的函数的结果和序列中的下一个元素做相关操作。
例子1:lambda函数实现将str转化为int
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))
例子2:将str转化为浮点数
digits = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
def str2float(s):
def fn(x, y):
return x * 10 + y
def char2num(s):
return digits[s]
# 用index查找小数点在数字的第几位,然后用整个长度减去这个长度就可以得到有多少个小数位
# 注意要多减1,因为小数点自身的位置也要减掉。
dot = len(s) - s.index('.') -1
# 去掉小数点
a = s.replace('.', '')
# 按照整数的方法来运算,最好再乘一个分位数,这样就转为float了。
return reduce(fn, map(char2num, a)) * 0.1**dot
(2) filter
filter把传入的函数作用于序列中的每个元素,根据返回值是True还是False,确定是保留还是舍弃该元素。例如下面的代码(实现将list中的空字符串去掉):
def not_empty(s):
return s and s.strip()
list(filter(not_empty, ['A', '', 'B', None, 'C', ' ']))# 结果: ['A', 'B', 'C']
由于filter()返回的是一个Iterator,和map一样,需要用list()函数获取结果并返回list。
例子:用filter筛选1-1000的回数
def is_palindrome(n):
return str(n) == str(n)[::-1]
output = filter(is_palindrome,range[1,1001])
print('1-1000:',list(output))
(3)sorted
用于排序。使用sorted,默认是按照从小到大的顺序排列的。
>>> sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21, 36]
接受key实现自定义排序。
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
['Zoo', 'Credit', 'bob', 'about']
reverse=True保证是按照正序排列的。
分别按照姓名、成绩进行排序,有
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
def by_name(t):
return t[0] #返回tuple内部第一个元素,也就是name
L1 = sorted(L,key = by_name)
print(L1)
def by_score(t):
return -t[1] #返回tuple内部第二个元素的负值,也就是score的负值
L2 = sorted(L,key = by_score)
print(L2)
2. 返回函数
闭包:
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum
>>> f = lazy_sum(1, 3, 5, 7, 9)
>>> f #返回的是函数,而不是结果。
<function lazy_sum.<locals>.sum at 0x101c6ed90>
>>> f() #此时函数才执行,得到结果。
25
我们在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种程序结构被称为“闭包”。
注:返回函数不要引用任何循环变量,或者后续会发生变化的变量。详见该部分的具体实例讲解!(自己还未弄明白)
3. 装饰器
可以动态地增加函数的功能,而且不改变原有的函数的定义。
这部分自己还未弄懂具体内容。
4. 偏函数
functools.partial来实现。
>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85
实际上,偏函数就是把函数的某些参数固定住(设置为默认参数),返回一个新的函数。常用于函数的参数比较多的情况,调用起来更加方便。
六、模块
一个.py文件就是一个模块。
可以用包来组织模块,避免模块名出现冲突。对于每一个包,都有一个_init_.py文件,用来说明是一个包。文件_init_py是一个模块,模块名是上层目录的名字。
注:模块名的命名遵循Python中的变量命名规则。
命名模块时不要和系统的模块相冲突,通过import 模块名来进行验证,如果没有错误的话则说明是有该模块的。
1. 使用模块
if __name__=='__main__':
test()
上述是模块hello中的一段代码。在其它地方导入hello模块时,上述的if判断将失效。
对于__xxx__这样的变量,是特殊变量。
若只想让函数和变量在模块内部使用,可以用_前缀来命名它们。例如__name__、__author__。对于_xxx或__xxx这样的变量是private的。正常情况下定义的函数或者变量是遵从变量命名规则的。
2. 安装第三方模块
pip install 名称