高级特性
切片
取list前三个元素:
L = ['diaochan','xiaoqiao','daqiao','wangzhaojun','daji']
print(L[0],L[1],L[2])
diaochan xiaoqiao daqiao
取前N个元素,可以用循环:
L = ['diaochan','xiaoqiao','daqiao','wangzhaojun','daji','caiwenji','yuji']
r = []
n = 5
for i in range(n):
r.append(L[i])
print(r)
['diaochan', 'xiaoqiao', 'daqiao', 'wangzhaojun', 'daji']
切片法比较简单,例如还是取前N个元素:
L = ['diaochan','xiaoqiao','daqiao','wangzhaojun','daji','caiwenji','yuji']
print(L[0:3])
['diaochan', 'xiaoqiao', 'daqiao']
L[0:3]表示,从索引0开始取,直到索引3为止,但不包括索引3。即索引0,1,2,正好是3个元素。
如果第一个索引是0,还可以省略:
L = ['diaochan','xiaoqiao','daqiao','wangzhaojun','daji','caiwenji','yuji']
print(L[:3])
['diaochan', 'xiaoqiao', 'daqiao']
也可以写成 L[1:3],取出2个元素。
倒数切片:
倒数第一个元素的索引是-1
L = ['diaochan','xiaoqiao','daqiao','wangzhaojun','daji','caiwenji','yuji']
print(L[-1])
print(L[-3:])
print(L[-3:-1])
yuji
['daji', 'caiwenji', 'yuji']
['daji', 'caiwenji']
还可以有以下操作:
L = list(range(20)) #创建数列
print(L[:10:2]) #前10个数,每两个取一个
print(L[::5]) #所有数,每5个取一个
print(L[:]) #复制一个list
[0, 2, 4, 6, 8]
[0, 5, 10, 15]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
tuple也是一种list,唯一区别是tuple不可变。因此,tuple也可以用切片操作,只是操作的结果仍是tuple:
L = (0,1,2,3,4,5,6,7,8,9)
print(L[:5:2])
print(L[::5])
print(L[:])
(0, 2, 4)
(0, 5)
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
字符串’xxx’也可以看成是一种list,每个元素就是一个字符。因此,字符串也可以用切片操作,只是操作结果仍是字符串,可以简单的写成如下形式:
print('ABCDEFG'[:3])
print('ABCDEFG'[::2])
ABC
ACEG
迭代
Python的for循环不仅可以用在list或tuple上,还可以作用在其他可迭代对象上。
d = {'a':1,'b':2,'c':3,'d':4}
for key in d:
print(key) #默认情况下
for value in d.values(): #迭代value
print(value)
for k,v in d.items(): #迭代key和value
print(k,v)
a
b
c
d
1
2
3
4
a 1
b 2
c 3
d 4
因为dict的存储不是按照list的方式顺序排列,所以,迭代出的结果顺序很可能不一样
for ch in 'ABCD':
print(ch)
A
B
C
D
判断一个对象是可迭代对象:通过collections模块的Iterable类型判断
from collections import Iterable
print(isinstance('abc', Iterable)) # str是否可迭代
print(isinstance([1,2,3], Iterable)) # list是否可迭代
print(isinstance(123, Iterable) )# 整数是否可迭代
True
True
False
对list实现下标循环:Python内置的enumerate函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身
enumerate:枚举
for i, value in enumerate(['A', 'B', 'C']): #同时引用了两个变量在Python里很常见
print(i, value)
0 A
1 B
2 C
引用两个变量的例子:
for a,b in [(1,1),(2,3)]:
print(a,b)
1 1
2 3
使用迭代查找一个list中最小和最大值,并返回一个tuple:
# -*- coding: utf-8 -*-
def findMinAndMax(L):
if L!=[]:
min = L[0]
max = L[0]
for i in L[1:]:
if max < i:
max = i
if min > i:
min = i
return (min,max)
else:
return(None,None)
# 测试
if findMinAndMax([]) != (None, None):
print('测试失败!')
elif findMinAndMax([7]) != (7, 7):
print('测试失败!')
elif findMinAndMax([7, 1]) != (1, 7):
print('测试失败!')
elif findMinAndMax([7, 1, 3, 9, 5]) != (1, 9):
print('测试失败!')
else:
print('测试成功!')
测试成功!
列表生成式
一般方法
一般方法:
print(list(range(1,10)))
[1, 2, 3, 4, 5, 6, 7, 8, 9]
或者循环方法:
L = []
for x in range(1,11):
L.append(x*x)
print(L)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
列表生成式
但是循环太麻烦,而列表生成式则可以用一行语句代替循环生成上面的list,写列表生成式时,把要生成的元素x * x放到前面,后面跟for循环,就可以把list创建出来:
print([x*x for x in range(1,11)])
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
可以加上判断
print([x*x for x in range(1,11) if x%2 == 0])
[4, 16, 36, 64, 100]
使用两层循环,生成全排列
print([x+y for x in 'ABC' for y in 'LMN'])
['AL', 'AM', 'AN', 'BL', 'BM', 'BN', 'CL', 'CM', 'CN']
列出当前目录下的所有文件和目录名
import os # 导入os模块,模块的概念后面讲到
print([d for d in os.listdir('.')]) # os.listdir可以列出文件和目录
['.anaconda', '.astropy', '.bash_history', '.conda', '.condarc', '.config', '.idlerc', '.ipython', '.keras', '.matplotlib', '.PyCharmCE2019.3', '.python_history', '.vscode', '1.mat', 'AppData', 'ApplicmCE2019.3', '.python_history', '.vscode', '1.mat', 'AppData', 'Application Data', 'Contacts', 'Cookies', 'Desktop', 'Documents', 'Docal Settings', 'Music', 'My Documents', 'NetHood', 'NTUSER.DAT', 'ntuwnloads', 'Favorites', 'Intel', 'IntelGraphicsProfiles', 'Links', 'Local Settings', 'Music', 'My Documents', 'NetHood', 'NTUSER.DAT'6b948c6eb}.TMContainer00000000000000000001.regtrans-ms', 'NTUSER.DAT{, 'ntuser.dat.LOG1', 'ntuser.dat.LOG2', 'NTUSER.DAT{2599cde1-271b-11e5-80c6-0026b948c6eb}.TM.blf', 'NTUSER.DAT{2599cde1-271b-11e5-80intHood', 'Recent', 'Saved Games', 'Searches', 'SendTo', 'Templates',c6-0026b948c6eb}.TMContainer00000000000000000001.regtrans-ms', 'NTUSER.DAT{2599cde1-271b-11e5-80c6-0026b948c6eb}.TMContainer00000000000000000002.regtrans-ms', 'ntuser.ini', 'OneDrive', 'Pictures', 'pip', 'PrintHood', 'Recent', 'Saved Games', 'Searches', 'SendTo',
'Templates', 'Videos', '「开始」菜单']
同时迭代两个变量
d = {'x':'a','y':'b','z':'c'}
for k,v in d.items():
print(k,'=',v)
x = a
y = b
z = c
使用两个变量来生成list
d = {'x':'a','y':'b','z':'c'}
print([k + '=' + v for k,v in d.items()])
['x=a', 'y=b', 'z=c']
把所有的字符串变成小写(or大写)
L = ['Hello','World']
print([s.lower() for s in L])
print([s.upper() for s in L])
['hello', 'world']
['HELLO', 'WORLD']
if … else
错误示例:
print([x for x in range(1, 11) if x % 2 == 0 else 0])
SyntaxError: invalid syntax
print([x if x % 2 == 0 for x in range(1, 11)])
SyntaxError: invalid syntax
for前面的部分是一个表达式,它必须根据x计算出一个结果。因此,考察表达式:x if x % 2 == 0,它无法根据x计算出结果,因为缺少else,必须加上else:
print([x if x % 2 == 0 else -x for x in range(1, 11)])
[-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]
上述for前面的表达式x if x % 2 == 0 else -x才能根据x计算出确定的结果。
可见,在一个列表生成式中,for前面的if … else是表达式,而for后面的if是过滤条件,不能带else。
如果list中既包含字符串,又包含整数,由于非字符串类型没有lower()方法,所以列表生成式会报错:
L = ['Hello', 'World', 18, None]
print([s.lower() for s in L])
IndentationError: unexpected indent
应该改为:
L = ['Hello', 'World', 18, None]
print([x.lower() for x in L if isinstance(x,str)])
['hello', 'world']
isinstance函数
isinstance函数可以判断一个变量是不是字符串
生成器
从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
方法一
generator:
通过next()函数获得generator的下一个返回值
g = (x * x for x in range(10))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
0
1
4
9
对比list:
print([x * x for x in range(10)])
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
区别:仅在于最外层的[]和()
使用for循环
g = (x * x for x in range(10))
for n in g:
print(n)
0
1
4
9
16
25
36
49
64
81
如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,可以用函数来实现。
例如:斐波拉契数列
1, 1, 2, 3, 5, 8, 13, 21, 34, …
def fib(max):
n,a,b = 0,0,1
while n < max:
print(b)
a,b = b,a + b
n = n+1
return 'done'
fib(int(input('请输入:')))
或者更规范:
def fib(max):
n,a,b = 0,0,1
while n < max:
print(b)
a,b = b,a + b
n = n+1
return 'done'
a = (input('请输入:'))
a = int(a)
fib(a)
结果:
请输入:5
1
1
2
3
5
将fib(int(input('请输入:')))
换成print(fib(int(input('请输入:')))),
最后的done可以被打印出来
原因:命令行的fib相当于代码中的print(fib)
注意:
a, b = b, a + b
等价于
t = (b, a + b) # t是一个tuple,但不必显式写出临时变量t就可以赋值。
a = t[0]
b = t[1]
方法二
如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator:
还以斐波拉契数列为例,把print(b)改为yield b:
def fib(max):
n,a,b = 0,0,1
while n < max:
yield b
a,b = b,a + b
n = n+1
return 'done'
print(fib(int(input('请输入:'))))
请输入:5
<generator object fib at 0x000000B3237ACA98>
generator和函数的执行流程不一样。
函数是顺序执行,遇到return语句或者最后一行函数语句就返回。
generator的函数在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
简单的例子:
def odd():
print('step 1')
yield 1
print('step 2')
yield(3)
print('step 3')
yield(5)
next(odd())
next(odd())
step 1
step 1
def odd():
print('step 1')
yield 1
print('step 2')
yield(3)
print('step 3')
yield(5)
print(next(odd()))
print(next(odd()))
step 1
1
step 1
1
def odd():
print('step 1')
yield 1
print('step 2')
yield(3)
print('step 3')
yield(5)
f = odd()
print(next(f))
print(next(f))
print(next(f))
print(next(f))
step 1
1
step 2
3
step 3
5
Traceback (most recent call last):
File "d:/python exercise/1.py", line 12, in <module>
print(next(f))
StopIteration
区别:赋值给f才是生成器,否则只是一个函数
odd不是普通函数,而是generator,在执行过程中,遇到yield就中断,下次又继续执行。执行3次yield后,已经没有yield可以执行了,所以,第4次调用next(f)就报错。
在循环过程中不断调用yield就会不断中断。当然要给循环设置一个条件来退出循环,不然就会产生一个无限数列出来。
注意:把函数改成generator后,基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代。
>>> for n in fib(6):
... print(n)
1
1
2
3
5
8
但是用for循环调用generator时,拿不到generator的return语句的返回值。因此必须捕获StopIteration错误,返回值包含在StopIteration的value中:
>>> g = fib(6)
>>> while True:
... try:
... x = next(g)
... print('g:', x)
... except StopIteration as e:
... print('Generator return value:', e.value)
... break
...
g: 1
g: 1
g: 2
g: 3
g: 5
g: 8
Generator return value: done
杨辉三角
答案1:
'每次根据上一个的L,修改,得到下一个L。··规律:每一个值等于它斜上方左右值想加'
'例如第四行第二个数是3,range(2)可以看出来i为0和1;即上一个L的L[0]+L[1]=3,L[1]+L[2]=3,因此yield返回的L=[1 3 3 1]'
'yield的功能相当于return'
def triangles():
L = [1]
while len(L)<5:
yield L
L = [1]+[L[i]+L[i+1] for i in range(len(L)-1)]+[1] '内部用for循环实现中间的数值相加,两边则单独加'
for n in triangles():
print(n)
[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
答案2:
'与答案1不同的是答案2没有保留第一位和最后一位为1,而是设置为0,然后计算L[0]+L[1]=1'
'例如第二行,上一个L=[0 1 0]但是不输出,下一行range(2),则i为0和1'
def triangles():
l = [1]
while True:
yield l
l = [0]+l+[0]
l = [l[i]+l[i+1] for i in range(len(l)-1)]
n = 0
results = []
for t in triangles():
results.append(t)
n = n+1
if n ==10:
break
for t in results:
print(t)
if results == [
[1],
[1, 1],
[1, 2, 1],
[1, 3, 3, 1],
[1, 4, 6, 4, 1],
[1, 5, 10, 10, 5, 1],
[1, 6, 15, 20, 15, 6, 1],
[1, 7, 21, 35, 35, 21, 7, 1],
[1, 8, 28, 56, 70, 56, 28, 8, 1],
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
]:
print('测试通过!')
else:
print('测试失败!')
[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]
[1, 5, 10, 10, 5, 1]
[1, 6, 15, 20, 15, 6, 1]
[1, 7, 21, 35, 35, 21, 7, 1]
[1, 8, 28, 56, 70, 56, 28, 8, 1]
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
测试通过!
注意:输入L = [1]+[1] print(L)
输出[1, 1]
迭代器
可以直接作用于for循环的数据类型有以下几种:
(1)集合数据类型,如list、tuple、dict、set、str等;
(2)generator,包括生成器和带yield的generator function。
这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。可以使用isinstance()判断一个对象是否是Iterable对象。
生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。可以使用isinstance()判断一个对象是否是Iterator对象。
生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。把list、dict、str等Iterable变成Iterator可以使用iter()函数:
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True
为什么list、dict、str等数据类型不是Iterator?
因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。
Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
小结:
凡是可作用于for循环的对象都是Iterable类型;
凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
Python的for循环本质上就是通过不断调用next()函数实现的。