关于python的一些高级特性

前言

用 python 差不多半年多了,从去年暑假开始接触,从开始的懵逼,到写了一些小爬虫总算入门之后,许多作业也是能用 python 就用 python,基本抛弃了 c++。但是还是有些过于急躁了,能够写一些简短的代码,但是对于 python 的很多特性都不知道或者忘记了,这里回去廖大教程复习一下,顺便记录下我觉得比较重要的地方。

开始

本文主要记录廖大教程中高级特性这一节的内容,并写下我的一些理解。在我看来,这些特性是很 pythonic 的,用在代码中很有 bigger 啊~

列表生成式(list comprehensions)

切片和迭代就不说了,这里直接先看一下列表生成式吧,从名字就能大概猜出这是生成列表的一些方法,比如:如何生成[1*1, 2*2, … ,10*10]?可以用循环不断向列表尾部添加元素,如果使用 pythonic 的方法,也就是列表生成式,则是:

>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

后面还能跟上 if 判断,例如:

>>> [x * x for x in range(1, 11) if x%2==0]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

这样,本来需要使用循环写4,5行的代码,使用一行就解决了,直观明了。

还能使用两个 for 循环生成全排列:

>>> [m + n for m in ‘abc’ for n in ‘xyz’]
[‘ax’, ‘ay’, ‘az’, ‘bx’, ‘by’, ‘bz’, ‘cx’, ‘cy’, ‘cz’]

这样如何添加 if 判断呢?可以在每个 for 语句后添加,或者在最后添加:

>>> [m + n for m in ‘abc’ if m < 'c' for n in 'xyz' if n < 'z'] ['ax', 'ay', 'bx', 'by'] >>> [m + n for m in ‘abc’ for n in ‘xyz’ if n < 'z' and m < 'c'] ['ax', 'ay', 'bx', 'by']

也可以同时在一个 for 语句中迭代多个变量,比如dict的items()可以同时迭代key和value:

>>> d = {‘x’: ‘a’, ‘y’: ‘b’, ‘z’: ‘c’ }
>>> [k + ‘=’ + v for k, v in d.items()]
[‘y=b’, ‘x=a’, ‘z=c’]

差不多就是这样了~

但是以前总是写 c++ ,这种思维模式很难改过来,只能慢慢在使用中熟悉这种语法,习惯了就能够在下意识中写出来了。

生成器(generator)

为什么要使用生成器?廖大的教程中说得很详细,这里再简述一下:

因为列表的内容放在内存中,而受到内存限制,列表的容量有限。

如果我们只访问极少的元素,那么存在极大的空间浪费。

而生成器可以一边迭代一边计算下一个值,理论上,该过程可以无限进行下去,并且不会占用大量内存。

这里只是简单介绍一下,更详细的请 google 哈~

如何创建生成器?第一种方法类似于前面讲到的列表生成式,只需要将[]改为()即可:

>>> 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

可以看到,方法上大致相同,[]得到的是一个已经得到所有值的列表,()得到的是一个生成器,它们都能使用 for 循环来迭代,但是生成器不能使用下标访问,并且只能被迭代一次,再次迭代则会有 stopiteration 的异常:

>>> for i in g:
… print(i)

0
1
4
9
16
25
36
49
64
81
>>> for i in g:
… print(i)

>>> next(g)
traceback (most recent call last):
file “”, line 1, in
stopiteration

不过当我们创建了一个generator后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不需要关心stopiteration的错误。

如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现,比如,著名的斐波那契数列:

def fib(max):
n, a, b = 0, 0, 1
while n < max: yield b a, b = b, a + b n = n + 1 return 'done'

关于 yield 这个关键字,我在刚学 python 的时候也纠结了很久,直到看到生成器的时候才大致明白,大家搜索一下就能大致明白了,我觉得这东西说起来麻烦,只说一两句又怕说错。廖大的教程中是这样说的:

函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

可能有点难理解,不过明白了就很好说了。

当然,函数中还可以添加 return,在一个 generator function 中,如果没有 return,则默认执行至函数完毕,如果在执行过程中 return,则直接抛出 stopiteration 终止迭代。

例如上面的例子,我们在迭代时发现并没有出现 ‘done’ 这串字符,是因为 return 的值被当作 exception 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迭代器(iterator)

可直接作用于 for 循环的对象被称为可迭代对象,可以用 isinstance() 函数判断是否为可迭代对象:

>>> from collections import iterable
>>> isinstance([], iterable)
true
>>> isinstance({}, iterable)
true
>>> isinstance(‘abc’, iterable)
true
>>> isinstance((x for x in range(10)), iterable)
true
>>> isinstance(100, iterable)
false

而可以被next()函数调用并不断返回下一个值的对象称为迭代器:iterator。当然,仍然可以使用isinstance()判断一个对象是否是iterator对象:

>>> from collections import iterator
>>> isinstance((x for x in range(10)), iterator)
true
>>> isinstance([], iterator)
false
>>> isinstance({}, iterator)
false
>>> isinstance(‘abc’, iterator)
false

通过上面两个例子,可以这样理解:生成器和 list,tuple,str 等都是 iterable 对象,生成器同时还是 iterator 对象,而 list 等不是。那么能否直接将 iterable 对象转换成 iterator 对象呢?

可以使用iter()函数:

>>> isinstance(iter([]), iterator)
true
>>> isinstance(iter(‘abc’), iterator)
true

其实,iterator 对象表示的是一个数据流,我们可以把这个数据流看做是一个有序序列,但却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以 iterator 的计算是惰性的,只有在需要返回下一个数据时它才会计算。iterator甚至可以表示一个无限大的数据流,但 list,tuple 什么的是不可能这样的。

更多关于 python 的一些高级特性相关文章请关注php中文网!

Posted in 未分类

发表评论