切片就是切割list或者tuple里面的元素,获取某个或某段元素。
>>> L = ['a', 'b', 'c', 'd', 'e']
>>> L[:1]
['a']
>>> L[:3]
['a', 'b', 'c']
>>> L[2:3]
['c']
>>>
L[a:b]
表示返回从第a
个元素到第b
个元素,不包含第b
个元素。 如果索引为0
的话可以省略。
同样支持倒数切片,倒数第一个元素是的索引是-1
>>> L[-2:-1]
['d']
>>> L[-1:]
['e']
>>> L[-4:-1]
['b', 'c', 'd']
>>>
切片操作不仅可以切割相邻的元素,还可以分段切割
>>> l = [1,2,3,4,5,6,7,8,9,10]
>>> l[::2]
[1, 3, 5, 7, 9]
>>> l[1:3:2]
[2]
>>>
L[a:b:c]
表示返回从第a
个元素到第b
个元素(不包含第b
个元素),每c
个元素取一个。
同样对tuple也支持,只是返回的结果格式还是tuple
对字符串也同样适用,返回的还是字符串
>>> str = '123456789'
>>> str[1:4]
'234'
>>> str[::3]
'147'
>>>
切片是一种很高能的操作。能随意截取list、tuple甚至字符串,相比于oc截取字符串的某一段,Python这种切片操作简单极了
用for
循环来遍历一个可遍历对象称为迭代
可遍历对象: list、 tuple、 dict、 set、str...
>>> l = [1, 2, 3]
>>> for a in l:
... print(a)
...
1
2
3
>>>
>>> tup = (1,2,3)
>>> for a in tup:
... print(a)
...
1
2
3
>>>
dict是key-value存储的,默认迭代的是key, 若想迭代value需要.values()
, 若想同事迭代key-value 需要.items()
。
dict的存储顺序是无序的,迭代结果的顺序并不一定按照顺序排列
>>> dict = {1:'a', 2:'b', 3:'c'}
>>> for d in dict: #默认迭代key
... print(d)
...
1
2
3
>>> for d in dict.values(): #迭代value
... print(d)
...
a
b
c
>>>
>>> for k, v in dict.items(): #迭代key-value
... print(k, v)
...
1 a
2 b
3 c
>>>
>>> sets = set([1,2,3,4,4,2])
>>> sets
{1, 2, 3, 4}
>>> for k in sets:
... print(k)
...
1
2
3
4
>>>
>>> str = 'abc'
>>> for s in str:
... print(s)
...
a
b
c
>>>
迭代一个list,还需要显示list的索引。在Java或oc中有for(int i = 0; i < list.count; i ++)
这样的for循环,打印i就是list的索引。 在Python中有enumerate()
函数,能拿到list的索引
>>> l = ['a','b','c']
>>> for i, s in enumerate(l):
... print(i, s)
...
0 a
1 b
2 c
>>>
迭代时两个变量,比如一个list里面放了tuple [(1,2),(3,4),(5,6)]
可以引入两个迭代变量,遍历内容。 注意点是tuple的元素个数必须固定,跟给定的迭代变量参数个数相同,否则会报错
for a, b in [(1,2),(3,4),(5,6)]
print(a, b)
# 结果
1 2
3 4
5 6
引入collections
模块的Iterable
做类型判断
>>> from collections import Iterable
>>> isinstance(10, Iterable) #整数不可迭代
False
>>> isinstance(10.4, Iterable) #浮点型不可迭代
False
>>> isinstance([10,11], Iterable) #list可以迭代
True
>>> isinstance({10:'a',11:'b'}, Iterable) #dict可以迭代
True
>>>
>>> isinstance({10,11}, Iterable) #set可迭代
True
List Comprehensions
列表生成式,用简单的代码生成复杂的list
生成[1,2,3,4]
使用list(range(1,5))
,x从1
到5
不包括5
>>> list(range(1,5))
[1, 2, 3, 4]
>>>
如果要生成[1*1, 2*2, 3*3, 4*4, 5*5]
的list的话怎么生成呢? 可以用for循环
>>> l = []
>>> for x in range(1,5):
... l.append(x * x)
...
>>> l
[1, 4, 9, 16]
如果要用列表生成式的话,只需要一行代码就能搞定
>>> [x * x for x in range(1,5)]
[1, 4, 9, 16]
>>>
列表生成式[x * x for x in range(1,5)]
可以这样理解:for
循环是生成元素的,for
循环生成的元素放到前面结果元素计算式x * x
中计算得到最终结果,结果本身就在[]
中,不需要append
了。
列表生成式还可以增加筛选条,比如,生成1-10(包括10)中所有偶数的平方组成的list
>>> [x * x for x in range(1,11) if x % 2 == 0]
[4, 16, 36, 64, 100]
筛选条件跟在for循环后面,筛选for循环生成的元素,让只有符合条件的元素进入结果元素计算式
中计算,组成最终的list
需求:列出两个list里元素的两两一起的所有组合
>>> [n + m for n in ['a','b','c','d'] for m in ['A','B','C','D']]
['aA', 'aB', 'aC', 'aD', 'bA', 'bB', 'bC', 'bD', 'cA', 'cB', 'cC', 'cD', 'dA', 'dB', 'dC', 'dD']
>>>
根据打印结果知道,其实就是循环嵌套。这是二层嵌套,其实也可以有n层嵌套,可以试试。下面是三次for循环,可以自己试试
>>> [n + m + x for n in ['a','b','c','d'] for m in ['A','B','C','D'] for x in '1234']
嵌套循环其实就是多个变量的列表生成式,一层循环的多个变量同样适用。比如迭代(遍历)dict,同时迭代出key-value,然后组成list
>>> [str(k) + v for k,v in {1:'a', 2:'b', 3:'c', 4:'d'}.items()] #str()是类型转换,迭代出来的k是int类型,要想与字符串拼接要先转成str类型
['1a', '2b', '3c', '4d']
>>>
列表生成式只能生成有一定规律的list,比如1-10的偶数平方list,1-100中能被3整除的list。每个元素都是能通过算法计算出来的。
当用到一个list时,为节省存储空间,不用创建出完整的list再去使用,可以在循环中不断推算出后面的元素。类似iOS中的UItableView,只创建显示到屏幕上的cell,在滑动过程中再去创建设置cell(其实是拿内存中闲置的cell,cell的循环机制)。这种边循环边计算的机制称为生成器(generator)
创建一个generator
方法很多,其中一种只要把生成式的[]
换成()
即可
>>> [x * x for x in range(1,10)] #列表生成式创建list
[1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> (x * x for x in range(1,10)) #创建generator
<generator object <genexpr> at 0x1025e08e0>
生成器是边循环边计算元素的,那怎么打印其中每个元素呢?有一个函数next()
>>> g = (x for x in range(3))
>>> g
<generator object <genexpr> at 0x1025e0938>
>>> next(g)
0
>>> next(g)
1
>>> next(g)
2
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
每次调用next(g)
的时候,计算出下一个元素的值,当超出list的长度时,就会抛出异常StopIteration(停止迭代)
这种方法读取元素实在是太low了,generator也是可迭代对象,完全可以用for循环迭代
>>> g = (x for x in range(3))
>>> for z in g:
... print(z)
...
0
1
2
>>>
有一种数列跟生成器运行机制类似,就是斐波拉契数列
,除了开始的两个1,每个数都是前两个数的和。这个数列就是边循环边计算出元素的。
可以用函数打印这样的一个数列,输出指定位数的斐波拉契数列
def test(num):
n, a, b = 0, 0, 1
while n < num:
print(b)
a, b = b, a + b
n = n + 1
这里有一种很有趣的赋值方式,在oc和java中看不到。就是上面函数中的n, a, b = 0, 0, 1
和a, b = b, a + b
,这是一种同时赋值的方法,=
右边的值赋值给=
左边相应位置的参数,每个参数赋值是同时进行的,他们的值都是这条赋值式上面时获得的值。
比如a, b = b, a + b
,在赋值的时候,a = b
和 b = a + b
是同时进行的,最终的结果都是在a = 1, b = 2
的前提下得到的。
>>> a = 1
>>> b = 2
>>> a, b = b, a + b
>>> a
2
>>> b
3
在其他语言中,这种赋值要引入第三变量。不然第二条赋值结果会因为第一条赋值操作而改变。
上文中的test(num)
函数变为生成器很简单,只需要把函数的print(b)
换成yield b
def test(num):
n, a, b = 0, 0, 1
while n < num:
yield b
a, b = b, a + b
n = n + 1
如果一个函数定义中包含了关键字yield
,那么这个函数不再是一个普通的函数,而是一个生成器
调用结果,返回的是一个generator对象:
>>> test(9)
<generator object test at 0x101f0f8e0>
>>>
在函数中添加了yield
,这个函数就变成了生成器。这时就改变了Python函数的执行顺序,Python函数是顺序执行的,遇到return或最后一行语句就返回。如果变成了生成器,执行顺序就变成了遇到yield
返回,下次执行会从上次返回yield
的位置处继续执行
例如定义函数
def test():
print(1)
yield 'a'
print(2)
yield 'b'
print(3)
yield 'c'
print(4)
yield 'd'
return 'END'
执行调用:
>>> test()
<generator object test at 0x101ae08e0> #test()函数是生成器generator
>>> a = test()
>>> next(a) #第一次调用
1
'a'
>>> next(a) #第二次调用
2
'b'
>>> next(a) #第三次调用
3
'c'
>>> next(a) #第四次调用
4
'd'
>>> next(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration: END
>>>
和第一种创建的生成器一样,用for循环遍历该生成器 test()
>>> for n in test():
... print(n)
...
1
a
2
b
3
c
4
d
>>>
发现了一个问题,for循环遍历拿不到return
的返回值END
。看上面的next(a)
调用,最后超出范围时抛出的错误 : END
, END
在这里面。
其实生成器generator的return值在异常错误StopIteration
的value
值里
用循环遍历test()
,并捕获异常,拿到return
返回的END
值
>>> a = test()
>>> while True:
... try:
... x = next(a)
... print(x)
... except StopIteration as e:
... print(e.value)
... break
...
1
a
2
b
3
c
4
d
END
>>>
总结:generator是在循环过程中不断计算出下一个元素的值,并在适当条件结束循环。对于函数改为的generator遇到return或者到函数的最后一行语句就是结束generator的指令。就是函数结束,generator结束
可以直接作用于for循环的对象为可迭代对象: Iterable
可迭代对象有:list(数组)
、tuple(元组)
、dict(字典)
、set(key的集合)
、str(字符串)
、generator(生成器/带yiel的函数)
引入collections
模块里的Iterable
可以判断一个对象是否是Iterable(可迭代对象)
>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance(100, Iterable)
False
>>>
可迭代对象里的generator
,不但可以作用于for循环,还可以被next()
不断调用并返回下一个值,直到抛出错误StopIteration
。这种可以被next()
函数调用不断返回下一个值得对象被称为迭代器Iterator
引入collections
模块里的Iterator
可以判断一个对象是否是Iterator(迭代器)
>>> from collections import Iterator
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance(10, Iterator)
False
>>>
可迭代对象里的生成器generator
是Iterator迭代器
其他的list(数组)
、tuple(元组)
、dict(字典)
、set(key的集合)
、str(字符串)
虽然是可迭代对象Iterable
,但不是Iterator(迭代器)
可以用iter()
函数把这些可迭代对象变为迭代器
>>> isinstance(iter({}), Iterator)
True
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter(10), Iterator) #int类型连可迭代对象都不是,无法转为迭代器
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
>>>
为什么list
这些可迭代对象不是迭代器呢?因为迭代器Iterator
是一个数据流,可以存放无限大的数据,只是一个有序序列,不可能知道他的长度,只能不断next()
计算下一个数据,只有在需要下一个数据的时候才去计算,是一种惰性计算。而list
等这类可迭代对象都是可知长度的,不能next()
循环计算下一个值。