TOC

列表解析式

    如果目前我们有一个[1,2,3,4,5]的列表,如果我们需要取得每一个元素的平方,并创建一个新的列表,以这个列表为基础再创建一个新列表,新列表的元素是,这个列表中每个元素的平方,我们一般都会使用for循环都方式来实现,就因为这种写法使用得太多了,所以Python提供了一种新语法糖,语法糖是简化语法的一种语法,这个语法糖也就是列表解析式或者说列表推导式;
    列表解析式是使用for循环来进行生成元素,并且还可以有一个if条件,最终返回一个新列表;
temp=[]
for i in l1:
    temp.append(i**2)
print(temp) # [1, 4, 9, 16, 25]
    上面这种使用for循环来实现的方法,其实有一种更好的方法,就是列表解析,上面这种方式,性能很差,列表解析比其快一倍不止,列表解析能根据列表快速高效的生成一个新列表,这就是列表解析的意义;
语法:[ expression for iter_var in iterable ] | [ expression for iter_var in iterable if cond_exper ]
print([ x**2 for x in range(0,11)]) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# 加入判断
print([ x**2 for x in range(0,11) if x >= 3 ]) # [9, 16, 25, 36, 49, 64, 81, 100]
# 多if
print([ x**2 for x in range(0,11) if x >= 3 if x % 2 == 0 ]) # [16, 36, 64, 100]
# 多and
print([ x**2 for x in range(0,11) if x >= 3 and x % 2 == 0 ]) # [16, 36, 64, 100]
# 多or
print([ x**2 for x in range(0,11) if x >= 3 or x % 2 == 0 ]) # [0, 4, 9, 16, 25, 36, 49, 64, 81, 100]
  • 注意一:一旦遇到需要从一个可迭代对象中构建一个新列表的时候,使用列表解析式是最好的,因为列表解析式,列表解析式是一种语法糖,Python编译器会对其进行优化,不会因为简写导致效率降低,反而Python对其还进行来优化提速;
  • 注意二:列表解析式中如果存在print语句,那么将返回一个元素为None的列表,因为print函数没有返回值,如果我们写str那么是可以的,因为str函数是有返回值的,但是依旧会返回一个列表;
  • 注意三:虽然列表解析式不能进行多分支操作,但是它支持and、or、if操作;
列表解析式进阶
    列表解析式,还可以使用多for循环,假设是是在一个列表解析式里面有两个for语句,那么其实他们是嵌套关系,第一个for循环是上层循环,第二个循环为内层循环,需要注意一点的是,因为列表解析式表达式里面,我们的值只能写一个,所以这个时候我们需要使用列表、元祖、字典等方式给它包裹起来,这样就形成了一个值,示例如下;
print([(x, y) for x in 'abc' for y in range(4) if y % 2 == 0]) # [('a', 0), ('a', 2), ('b', 0), ('b', 2), ('c', 0), ('c', 2)]
# if条件可以随意挪动,结果都一样
print([(i,j) for i in range(7) if i > 4 for j in range(20,25) if j > 23])  # [(5, 24), (6, 24)] 
print([(i,j) for i in range(7) for j in range(20,25) if i > 4 if j > 23])  # [(5, 24), (6, 24)]
print([(i,j) for i in range(7) for j in range(20,25) if i > 4 and j > 23]) # [(5, 24), (6, 24)]
  • 重点:列表解析式的特性,立即计算,返回的不是迭代器,是一个可迭代对象,从头到尾,可以回头;

集合解析式

    集合解析式和列表解析式基本上一样,集合解析式里面返回一个集合,需要注意的是,列表解析式的值是随便的,可以是列表、元祖、字典、集合等,但是集合解析式就不一样了,需要返回一个可hash的值,列表就不可hash,所以在集合解析式里面不能写列表;
    语法:{ expression for iter_var in iterable } | { expression for iter_var in iterable if cond_exper }
s = {x for x in range(10) if x % 2 == 0}
print(s) # {0, 8, 2, 4, 6}
  • 重点:集合的key不可重复,重复时覆盖,最后一个为最终值;

字典解析式

    字典解析式和列表解析式基本上一样,没什么没太大的区别,特性也是一样,字典解析式里面返回一个集合,需要注意的是,列表解析式的值是随便的,可以是列表、元祖、字典、集合等,但是字典解析式就不一样了,字典的Key需要返回一个可hash的值,列表就不可hash,所以在集合解析式里面不能写列表;
d = {x:x+1 for x in range(10) if x % 2 == 0}
print(d) # {0: 1, 8: 9, 2: 3, 4: 5, 6: 7}

# 字典的key是不可重复的,当重复的时候以最后一个为准,因为后面一个会覆盖前面一个,所以可以看到value都是3
d = {x:y for x in range(3) for y in range(4)}
print(d) # {0: 3, 1: 3, 2: 3}
  • 重点:字典的key不可重复,重复时覆盖,最后一个为最终值;

生成器表达式

    生成器是一个可迭代对象,它是一个迭代器,迭代器顾名思义就是一个可以迭代的器,也就是说它天生就是用来迭代的,所以迭代器一定是一个可迭代对象,但是可迭代对象不一定是迭代器,它们是有一定的差异的;
    列表解析式,和生成器表达器,它们的语法几乎一样,只差在括号上,它不叫元祖解析式,叫做生成器表达式,生成器是Python中一个非常有用的东西,它是惰性求值的,需时计算,不需要的时候,它只是一个表达式,只有需要的时候,才会计算结果,并且只计算一个结果,不会直接全部计算,它不需要立即生成所有值,所以对内存及CPU对压力及小;
    而对于列表解析式它会立即创建出所有符合规范的数据并存放在内存当中,极大的消耗内存,而且列表解析式的数据没有计算完成,线程就会阻塞;
    生成器表达式,会为我们创建出一个生成器对象,生成器对象就是惰性求值的,并且它还是一个可迭代对象,我们需要一个值的时候,计算一下就行了,需要下一个值的时候,再计算一下就好了,类似range一样, 需要的时候,要一个算一个,不会立即生成所有的值;
语法:( expression for iter_var in iterable ] | [ expression for iter_var in iterable if cond_exper )
next函数
    对于Python的next函数而言,它的主要实现就是求一个可迭代对象的值,一次next返回一个可迭代对象的的值,当可迭代对象的值已经计算完成了,就会抛出StopIteration异常;
    因为生成器表达式,会创建一个生成器对象,这个生成器对象也是一个可迭代对象,所以我们就可以使用next函数来对这个生成器对象进行迭代求值;
x = (x for x in range(10) if x % 2 == 0)
print(next(x))  # 0
print(next(x))  # 2
print(next(x))  # 4
    既然它是一个可迭代对象,那么我们也可以使用for循环来进行迭代,其实内部和next是一个原理;
x = (x for x in range(10) if x % 2 == 0)
for i in x:
    print(i)
# 0
# 2
# 4
# 6
# 8
  • 重点:生成器表达式的特性,惰性求值,延迟计算,返回一个生成器对象,同时也是可迭代对象,可以使用next函数来进行依次求值,也可以使用for循环进行迭代,从头到尾不可回头;

列表解析式和生成器表达式对比

    列表解析式和生成器表达式,虽然在语法上只有一个符号不一样,但是他们在本质上其实区别非常非常大,我们可以认为列表解析式就是用来如何去创建一个序列,而对于生成器表达式来讲,它也是创建一个序列,但是他们不同的是,列表解析式是直接将序列创建出来,而生成器表达式则是给出一个公式,当你需要的时候,才会进行计算,也就是惰性求值的原理,他们的区别大致如下;
计算方式:生成器表达式采用延迟计算,列表解析式立即计算;
内存占用:生成器表达式非常省内存,它没有数据只是一个表达式,列表解析式是直接返回一个新列表,在量大的情况下极大的占用内存;
计算速度:生成器表达式计算耗时非常短,因为每次只需要求一个值,而列表解析式需要一次性返回所有的值,所以列表解析式更慢;

迭代

    对于迭代一词来讲,用简单的术语描述就是,它是从某个容器(比如list、tuple等),取出一个元素的过程,当我们使用一个循环来遍历某个容器的时候,就叫做迭代。它是这个过程本身的名字。

可迭代对象

    可迭代对象能够通过迭代,一次次的返回不同的对象,比如对于一个列表而言,返回的就是不同索引位置上面的值,需要注意的是,可迭代未必有序,同时,一个可迭代对象,一般都会定义两个方法或者其中一个方法,即__iter__和__getitem__方法;
    常见的可迭代对象有list、tuple、string、bytes、bytearray、range对象、set、dict、生成器、迭代器等,他们都可以使用成员操作符in、not in来进行成员判断,in本质上对于线性数据结构就是遍历对象,非线性结构求hash值;

迭代器

    迭代器是一种特殊可迭代对象,它支持next方法,如果一个可迭代对象不是一个迭代器,我们想将其变成迭代器,可以使用iter方法,就可以把一个可迭代对象封装成迭代器,使其支持迭代器协议,只能向前,不能后退;

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注