Python高级特性
Python高级特性 切片 迭代 列表生成式 生成器
一、切片
切片:数据的分段切割。如下定义一个列表然后获取前五位数据。
格式:对象[起始位置:需要读取的数量:间隔]
1 >>> olist = list(range(10)) 2 >>> 3 >>> olist 4 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 5 >>> olist[0:5] 6 [0, 1, 2, 3, 4] 7 >>> 8 >>> olist[0:5:2] 9 [0, 2, 4] 10 >>>
定义并且声明一个olist列表对象,然后通过切片返回一个新的列表对象。其实位置默认是0,并且可以省略。如olist[:5]。间隔默认是1。
range是一个系统函数,返回一个特定的有序的自然数,默认从零开始。
1 1 >>> help('range') 2 2 Help on class range in module builtins: 3 3 4 4 class range(object) 5 5 | range(stop) -> range object 6 6 | range(start, stop[, step]) -> range object 7 7 | 8 8 | Return an object that produces a sequence of integers from start (inclusive) 9 9 | to stop (exclusive) by step. range(i, j) produces i, i+1, i+2, ..., j-1. 10 10 | start defaults to 0, and stop is omitted! range(4) produces 0, 1, 2, 3. 11 11 | These are exactly the valid indices for a list of 4 elements. 12 12 | When step is given, it specifies the increment (or decrement). 13 13 |
对于列表而言支持反向读取,如olist[-1],获取最后一条数据。那么对于切片slice而言是否支持???
1 >>> olist = list(range(10)) 2 >>> olist[-2: -1] 3 [8] 4 >>> olist[-8: -1] 5 [2, 3, 4, 5, 6, 7, 8] 6 >>> 7 >>> olist[-1] 8 9 9 >>>
从测试结果来看是支持反向切片。但是反向之后切记最后一位角标是-1。
之上的测试是对列表List进行的,那么我接下来看下对于String和tuple是否也适用!!!
1 >>> oTuple = (1,2,3,4,5,6) 2 >>> oTuple[0:5:2] 3 (1, 3, 5) 4 >>> 5 >>> 6 >>> str = 'abcdefg' 7 >>> str[0:5:2] 8 'ace' 9 >>>
从执行结果来看和我们的猜想是完全一致的。到这里可能有人会问了那么既然元组和列表及字符串都可以这么用,那么集合set和字典dict那???其实是不可以的,因为对于集合和dict而言是没有下标的。
1 >>> oset = set([1,2,3]) 2 >>> oset 3 {1, 2, 3} 4 >>> 5 >>> oset[0:2] 6 Traceback (most recent call last): 7 File "<stdin>", line 1, in <module> 8 TypeError: 'set' object is not subscriptable 9 >>> 10 >>> odict = {'a:1','b:2','c:3'} 11 >>> odict[0:2] 12 Traceback (most recent call last): 13 File "<stdin>", line 1, in <module> 14 TypeError: 'set' object is not subscriptable 15 >>>
二、迭代
迭代其实就是对集合<List, Tuple, dict, set等>的遍历。这里仅仅是列举了一些常见的集合,其实这里只要是Iterable的子类都可以使用迭代器来实现遍历。判断如下:
1 >>> oSet = set([1,3,4,5,]) 2 >>> oSet 3 {1, 3, 4, 5} 4 >>> for item in oSet: 5 ... print(item) 6 ... 7 1 8 3 9 4 10 5 11 >>> isinstance(oSet, Iterable) 12 True 13 >>> index = 2 14 >>> 15 >>> isinstance(index, Iterable) 16 False 17 >>>
上述采用的是系统函数isinstance()完成对对象的判断,判断给对象是否是Iterable的子类。按照规定只要是Iterable的子类就可以直接使用for循环实现迭代。
在使用isinstance方法时候判断是否是Iterable子类的时候,请记住请优先执行导入Iterable操作,默认isinstance方法是在collections类中。故而执行应该在文件头部增加from collections import Iterable。
这里重点讲解下迭代器如何在迭代中获取index角标和dict遍历。
1 >>> olist = ['A','B','C','E'] 2 >>> for index, item in enumerate(olist): 3 ... print(index, ' : ', item) 4 ... 5 0 : A 6 1 : B 7 2 : C 8 3 : E 9 >>>
该出使用的系统方法enumerate(),该方法可以实现把集合变成角标-元素。
字典集合遍历方式一获取key:
1 >>> oDict = {'a:1','b:2','c:3'} 2 >>> 3 >>> oDict 4 {'b:2', 'a:1', 'c:3'} 5 >>> 6 >>> for item in oDict: 7 ... print(item) 8 ... 9 b:2 10 a:1 11 c:3 12 >>>
字典集合遍历方式二获取value:
1 >>> oDict = {'a':1,'b':2,'c':3} 2 >>> oDict 3 {'a': 1, 'b': 2, 'c': 3} 4 >>> for value in oDict.values(): 5 ... print(value) 6 ... 7 1 8 2 9 3 10 >>>
字典集合遍历方式三获取key-value:
1 >>> oDict = {'a':1,'b':2,'c':3} 2 >>> oDict 3 {'a': 1, 'b': 2, 'c': 3} 4 >>> for k, v in oDict.items(): 5 ... print(k, " = ", v) 6 ... 7 a = 1 8 b = 2 9 c = 3
三、列表生成式
列表生成式:顾名思义就是生成对应的列表。场景如定义一个自然数1-100放入到指定的集合等。使用方式就是使用系统函数range,前边已经关于这个方法有过说明,这里就直接跳过。
range仅仅是获取有序的自然数,假如我们需求有天发生了变化,如获取1-100的平分数,然后放到指定集合,当下range就有点尴尬了。不过好在Python为我们做了这样的定制开发。一起走进代码。
1 >>> oList = list(range(1,5)) 2 >>> oList 3 [1, 2, 3, 4] 4 >>> 5 >>> nList = [x * x for x in range(1,5)] 6 >>> nList 7 [1, 4, 9, 16] 8 >>>
第一行到第四行是获取自然数1-5存放于指定集合。第五行到第七行是对自然数1-5开平方然后存放于指定集合中。
上述内容细节还可以进一步优化,加入仅仅保留偶数的平方,那么我们可以这么干。
1 >>> nList = [x * x for x in range(1,5) if x % 2 == 0] 2 >>> nList 3 [4, 16] 4 >>>
列表生成式实现排列组合,666。。。<又名for循环嵌套>
1 >>> nList = [m + n for m in 'ADC' for n in 'MVP'] 2 >>> nList 3 ['AM', 'AV', 'AP', 'DM', 'DV', 'DP', 'CM', 'CV', 'CP'] 4 >>>
这里其实就是嵌套for循环。如何不理解可以换个方式去看。
1 >>> nList = [] 2 >>> for m in 'ADC': 3 ... for n in 'MVP': 4 ... nList.append(m + n) 5 ... 6 >>> nList 7 ['AM', 'AV', 'AP', 'DM', 'DV', 'DP', 'CM', 'CV', 'CP'] 8 >>>
看着这些代码是不是更好理解怎么肥事。
四、生成器<Generator>
生成器可以理解为对生成器的优化,这里优化主要是说在内存上的优化。举例子来说,假如需要数据有序的自然数零到一万存放于列表中,这时可以有两种方式,一是直接生成一刀一万的自然数放到内存中。另一种是用到那个就读那个到内存中。很显然第二种方式在内存这块要优于第一种,这就是我们要说的生成器。
生成器声明格式和列表生成式大致相同,区别在于外围中括号和括号去别。看下生成器在代码中的使用,以及数据获取、
1 >>> oGenerator = (n for n in range(1, 5)) 2 >>> oGenerator 3 <generator object <genexpr> at 0x0000000002D60BF8> 4 >>> next(oGenerator) 5 1 6 >>> next(oGenerator) 7 2 8 >>> next(oGenerator) 9 3 10 >>> next(oGenerator) 11 4 12 >>> next(oGenerator) 13 Traceback (most recent call last): 14 File "<stdin>", line 1, in <module> 15 StopIteration 16 >>>
第一行创建了一个生成器对象。直接该对象。返回了个这'<generator object <genexpr> at 0x0000000002D60BF8>',What???一脸懵逼。。。好吧暂且把它看做是python对一个对象的表达形式吧,就如同java中对象打印一样,获得是对应的hashcode值。
next()读取生成器中的数据的系统方法。该方法获取生成器中的数据遇到yield则执行数据返回。yield的功能等同于return,不同点在于yield挂机程序执行,并不会结束方法执行。
这里对于方法next()使用不是绝对安全的,在地十二行处调用next()方法抛出了一个StopIteration异常,意思就是已经是最后一个数据了,没有更多数据了。那么怎么避免这个问题那???也许有些人想到了for循环,那么对于这个generator是否也适用???我们有我们的办法,isinstance()。想到了就去做。
1 >>> isinstance(oGenerator, Iterable) 2 True 3 >>>
如上代码证明了Generator生成器其实也是Iterable的子类,那么这就简单了。
>>> oGenerator = (n for n in range(1, 5)) >>> for item in oGenerator: ... print(item) ... 1 2 3 4 >>>
到了这里我们也就发现了正确的读取生成器Generator中数据的方法。
下一步就是如何自定义生成器????
1 >>> def step(): 2 ... print('step 1') 3 ... yield 1 4 ... print('step 2') 5 ... yield 6 6 ... print('step 3') 7 ... yield 12 8 ... 9 >>> 10 >>> g = step() 11 >>> g 12 <generator object step at 0x00000000024D2EB8> 13 >>> next(g) 14 step 1 15 1 16 >>> next(g) 17 step 2 18 6 19 >>> next(g) 20 step 3 21 12 22 >>>
自定义Generator适用关键字yield,方法在执行next()过程中,每当遇到yield都会直接返回,但是方法并未真正结束,仅仅是暂停。当你再一次调用next()则会在当前暂停出继续执行,知道执行完全结束,最后抛出StopIteration异常。
这里演示过程中采用了next()方法遍历自定义的Generator,在真正使用过程中尽量使用for循环,因为for内部会处理StopIteration异常。