慕课网Python3入门课程笔记

Python变量和数据类型

数据类型

  • 整数和浮点数:在计算机内部存储的方式是不同的,整数运算永远是精确的(除法也是精确的),而浮点数运算则可能会有四舍五入的误差;
  • 布尔值:用TrueFalse表示(注意大小写);
  • 空值:用None表示,None不能理解为0,因为0是有意义的,而None是一个特殊的空值。

Print语句

print语句也可以跟上多个字符串,用逗号“,”隔开,print会依次打印每个字符串。每个逗号“,”会输出一个空格

注释

Python的注释以#开头,后面的文字直到行尾都算注释。

变量

  • 变量名必须是大小写英文、数字和_的组合,且不能用数字开头;
  • 动态语言:变量本身类型不固定的语言,例如Python;
  • 常量:在Python中,通常用全部大写的变量名表示常量,但事实上仍然是一个变量

定义字符串

字符串可以用'...'或者"..."括起来表示。

  1. 如果字符串本身包含',可以用"..."括起来表示;
  2. 如果字符串本身包含",可以用'...'括起来表示;
  3. 如果字符串既包含'又包含",使用转义字符\

注意:Python区分大小写!

raw字符串和多行字符串

raw字符串

在字符串前面加个前缀r, 表示这是一个raw字符串,里面的字符就不需要转义。例如:

1
2
>>>print(r'\n\t\\')
\n\t\\

多行字符串

多行字符串,可以用'''...'''表示:

1
2
3
'''Line1
Line2
Line3'''

上面这个字符串的表示方法和下面的是完全一样的:

1
'Line 1\nLine 2\nLine 3'

:还可以在多行字符串前面添加r,把这个多行字符串也变成一个raw字符串:

1
2
3
r'''Python is created by "Guido".
It is free and easy to learn.
Let's start learn Python in imooc!'''

字符串编码

几种常见的编码

  • GB2312:国标简体中文编码;
  • GBK:GBK是包括中日韩字符的大字符集合,GB2312是GBK的子集;
  • Unicode:Unicode把所有语言都统一到一套编码里;
  • UTF-8:UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,是可变长编码;

默认情况下,Python3源码文件中使用UTF-8编码,在内存中使用Unicode编码,即Python3中的字符串是Unicode编码的。

Unicode编码与其他编码的转换

Unicode编码转换成其他编码时,使用encode方法,参数值为要转换成的编码类型:

1
2
3
4
>>> '中文'.encode('gb2312')
b'\xd6\xd0\xce\xc4'
>>> '中文'.encode('utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'

其他编码转换成Unicode编码时,使用decode方法,参数值为原编码类型:

1
2
3
4
5
6
7
>>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
'中文'
>>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('gb2312')
Traceback (most recent call last):
File "<pyshell#96>", line 1, in <module>
b'\xe4\xb8\xad\xe6\x96\x87'.decode('gb2312')
UnicodeDecodeError: 'gb2312' codec can't decode byte 0xad in position 2: illegal multibyte sequence

数据类型

python中数有四种类型:整数、长整数、浮点数和复数。

  • 整数:如1, 二进制0b1001, 十六进制0x2E
  • 长整数:是比较大的整数;
  • 浮点数:如1.233E-2
  • 复数:如1+2j1.1+2.2j

布尔类型

  1. Python把0None空字符串、空list、空tuple、空dict和空set看成False其他数值非空数据类型都看成True
  2. and 和 or 运算的重要法则——短路计算
    • 在计算a and b时,如果 a 是 False,则根据与运算法则,整个结果必定为 False,因此返回 a;如果 a 是 True,则整个计算结果必定取决与 b,因此返回 b;
    • 在计算a or b时,如果 a 是 True,则根据或运算法则,整个计算结果必定为 True,因此返回 a;如果 a 是 False,则整个计算结果必定取决于 b,因此返回 b。

:利用这一点,可以通过 or 运算,把空字符串“变成”默认字符串,而非空字符串保持不变

List和Tuple类型

List

创建list

list是一种有序的列表,可以随时添加和删除其中的元素。用 [ ]把list的所有元素括起来就构造了一个list对象。list中包含的元素可以是不同种数据类型。一个元素也没有的list是空list。list中元素可重复

按照索引访问list

  1. 索引从 0 开始,格式为L[0]
  2. 使用索引时,千万注意不要越界,否则报错IndexError

倒序访问list

  1. 用 -1 这个索引来表示最后一个元素,即L[-1];类似的,倒数第二用 -2 表示,倒数第三用 -3 表示。
  2. 使用倒序索引时,也要注意不要越界,否则同样报错IndexError

添加新元素

  • 用 list 的append()方法把新元素添加到 list 的尾部

    1
    2
    3
    4
    >>> L = ['Adam', 'Lisa', 'Bart']
    >>> L.append('Paul')
    >>> print(L)
    ['Adam', 'Lisa', 'Bart', 'Paul']
  • 用list的insert()方法把新元素添加到除尾部的其他位置,它有两个参数,第一个参数是索引位置,第二个参数是待添加的新元素;

    1
    2
    3
    4
    >>> L = ['Adam', 'Lisa', 'Bart']
    >>> L.insert(0, 'Paul')
    >>> print(L)
    ['Paul', 'Adam', 'Lisa', 'Bart']

注意:使用insert()方法,,可以认为是插入到了索引位置的前面。使用倒序索引时注意:

1
2
3
4
>>> L = [1, 2, 4]
>>> L.insert(-1, 3)
>>> print(L)
[1, 2, 3, 4] #元素插入到了倒序索引位置为-1的前面

从list删除元素

  • 用list的pop()方法删掉list的末尾元素,返回值是被删掉的元素;

    1
    2
    3
    4
    5
    >>> L = ['Adam', 'Lisa', 'Bart', 'Paul']
    >>> L.pop()
    'Paul'
    >>> print(L)
    ['Adam', 'Lisa', 'Bart']
  • 用list的pop(x)方法删掉list的任意位置元素,参数x为索引(可倒序索引),返回值是被删掉的元素;

    1
    2
    3
    4
    5
    >>> L = ['Adam', 'Lisa', 'Paul', 'Bart']
    >>> L.pop(-2)
    'Paul'
    >>> print(L)
    ['Adam', 'Lisa', 'Bart']

替换元素

对list中的某一个索引(可倒序索引)赋值,就可以直接用新的元素替换掉原来的元素,list包含的元素个数保持不变。

1
2
3
4
>>> L = ['Adam', 'Lisa', 'Bart']
>>> L[-1] = 'Paul'
>>> print(L)
L = ['Adam', 'Lisa', 'Paul']

Tuple

创建tuple

tuple是另一种有序的列表,中文翻译为“元组”。创建tuple使用()。tuple一旦创建完毕就不能修改。tuple没有append()方法,也没有insert()和pop()方法。可以使用索引方式访问元素,但是不能对其中的元素赋值

创建单元素tuple

  1. 包含 0 个元素的 tuple,也就是空tuple,直接用 ()表示,即t = ()
  2. 因为()既可以表示tuple,又可以作为括号表示运算时的优先级,用()定义单元素的tuple有歧义。所以 Python 规定,单元素 tuple 要多加一个逗号“,”避免歧义:
1
2
3
>>> t = (1,)
>>> print(t)
(1,)

“可变”的tuple

tuple所谓的“不变”是说,tuple的每个元素,指向永远不变,即指向’a’,就不能改成指向’b’。指向一个list,就不能改成指向其他对象,但指向的这个list本身是可变的!

1
2
3
4
5
>>> t = ('a', 'b', ['A', 'B'])
>>> L = t[2]
>>> L[0] = 'X'
>>> print(t)
('a', 'b', ['X', 'B'])

条件判断和循环

Python代码的缩进规则

  1. 具有相同缩进的代码被视为代码块。
  2. 缩进为4个空格,不要使用Tab,更不要混合Tab和空格,否则很容易造成因为缩进引起的语法错误。

条件判断语句

if语句

if 语句后接条件表达式,然后用:表示代码块开始。

if-else

如果条件判断是“非此即彼”的,即要么符合条件1,要么符合条件2,可以用一个if ... else ...语句把它们统一起来。根据条件表达式的值为 True 或者 False ,分别执行 if 代码块或者 else 代码块。注意else后面有个“:”

if-elif-else

1
2
3
4
5
6
7
8
if <条件判断1>:
<执行1>
elif <条件判断2>:
<执行2>
elif <条件判断3>:
<执行3>
else:
<执行4>

注意:这一系列条件判断会从上到下依次判断,如果某个判断为 True,执行完对应的代码块,后面的条件判断就直接忽略,不再执行了。

循环

for…in循环 & range()函数

1
2
3
names = ['Michael', 'Bob', 'Tracy'] #names也可以是tuple类型
for name in names:
print(name)

name 这个变量是在 for 循环中定义的,意思是,依次取出list中的每一个元素,并把元素赋值给 name,然后执行for循环体(就是缩进的代码块)。

range(x)函数生成一个整数序列list,元素是从0开始到x-1的整数。例如求1-100的整数和:

1
2
3
4
sum = 0
for x in range(101):
sum = sum + x
print(sum)

while循环

while 循环不会迭代 list 或 tuple 的元素,而是根据表达式判断循环是否结束。while循环每次先判断条件表达式,如果为True,则执行循环体的代码块,否则,退出循环。

1
2
while <条件判断>:
<执行>

注意:要特别留意while循环的退出条件。

break退出循环

用 for 循环或者 while 循环时,如果要在循环体内直接退出循环,可以使用 break 语句。break语句生效时,其后面的循环体语句将不会执行。

continue继续循环

在循环过程中,可以用continue跳过后续循环代码,结束本次循环,继续下一次循环。continue语句生效时,其后面的循环体语句将不会执行。

多重循环

在循环内部,还可以嵌套循环。

Dict和Set类型

Dict

什么是dict

dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度。dict用{ }表示,元素按照key: value写出来即可。最后一个key: value的逗号可以省略。

1
2
3
4
5
d = {
key1: value1,
key2: value2,
key3: value3
}

访问dict

  • 使用d[key]的形式来查找对应的 value,如果key 存在,dict就返回对应的value。这和 list 的不同之处是,list 使用索引返回对应的元素,而dict使用key
    注意:使用时要先用in操作符判断一下 key 是否存在,否则会报错KeyError

    1
    2
    3
    4
    5
    6
    7
    d = {
    'Adam': 95,
    'Lisa': 85,
    'Bart': 59
    }
    if 'Paul' in d: #使用in运算符判断key存在否
    print(d['Paul'])
  • 使用dict提供的一个get方法,在Key不存在的时候,返回None或者自己指定的value。

    1
    2
    3
    4
    5
    6
    >>> print(d.get('Bart'))
    59
    >>> print(d.get('Paul'))
    None
    >>> print(d.get('Paul', -1))
    -1

dict的特点

  1. 查找速度快。无论dict有10个元素还是10万个元素,查找速度都一样。但代价是dict 占用内存大。(由于dict是按 key 查找,所以key不能重复。)

  2. 存储的key-value序对是没有顺序的。print的顺序不一定是我们创建时的顺序,而且,不同的机器打印的顺序都可能不同,这说明dict内部是无序的,不能用dict存储有序的集合

  3. key只能是不可变对象。Python的基本类型如字符串、整数、浮点数都是不可变的,都可以作为key。但是list是可变的,就不能作为key。

更新dict

  • 添加元素:可以用d[NewKey] = NewValue往dict中添加新的 key-value。如果 key 已经存在,则赋值会用新的 value 替换掉原来的 value。

    1
    2
    3
    4
    5
    6
    7
    8
    d = {
    'Adam': 95,
    'Lisa': 85,
    'Bart': 59
    }
    >>> d['Paul'] = 72 #添加新元素
    >>> print(d)
    {'Lisa': 85, 'Paul': 72, 'Adam': 95, 'Bart': 59}
  • 删除元素:要删除一个key,用pop(key)方法(返回值是value),对应的value也会从dict中删除:

    1
    2
    3
    4
    >>> d.pop('Paul')
    72
    >>> d
    d = {'Adam': 95, 'Lisa': 85, 'Bart': 59}

遍历dict

直接使用for循环可以遍历 dict 的 key。又由于通过 key 可以获取对应的 value,因此在循环体内可以获取到value的值。

1
2
3
4
5
6
7
>>> d = {'Adam': 95, 'Lisa': 85, 'Bart': 59}
>>> for key in d:
... print(key + ':', d[key])
...
Lisa: 85
Adam: 95
Bart: 59

Set

什么是set

set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中key不重复、无序且是确定的。set相当于数学上定义的集合。

创建 set 的方式是使用set()函数并传入一个可迭代对象,可迭代对象的元素将作为set的元素。当包含重复元素时,set会自动去掉重复的元素。

1
2
3
4
5
>>> s = set(['A', 'B', 'C', 'C'])
>>> print(s)
{'A', 'C', 'B'}
>>> len(s)
3 #重复的元素被删除

注意:上述打印的形式类似 list,但它不是 list;打印的顺序和原始 list 的顺序有可能是不同的,因为set内部存储的元素是无序的

遍历set

由于 set 也是一个集合,所以,遍历 set 和遍历 list 类似,通过 for 循环实现。

注意: for循环在遍历set时,元素的顺序和list的顺序很可能是不同的,而且不同的机器上运行的结果也可能不同。

更新set

更新set主要做两件事:

  1. 添加元素:用set的add()方法。可以直接添加,元素已经存在则不会添加。
  2. 删除元素:用set的remove()方法。如果删除的元素不存在会报错KeyError,因此remove()前需要判断。

函数

调用函数

调用一个函数,需要知道函数的名称参数。调用函数的时候,如果传入的参数数量不对,或者参数类型不能被函数所接受,会报TypeError的错误。

编写函数

定义函数要使用def语句,依次写出函数名括号、括号中的参数冒号,然后,在缩进中编写函数体,函数的返回值用return语句返回。

注意

  1. 函数体内部的语句在执行时,一旦执行到return时,函数就执行完毕,并将结果返回;
  2. 如果没有return语句,函数执行完毕后也会返回结果,只是结果为None
  3. return None可以简写为return

返回多值

在函数体中,使用return x, y的形式来返回多个值,实际返回值是一个tuple

1
2
3
4
5
import math
def cal(len, angle):
nx = len * math.cos(angle)
ny = len * math.sin(angle)
return nx, ny

这样我们就可以同时获得返回值:

1
2
3
>>> x, y = cal(10, math.pi / 6)
>>> print(x, y)
8.66025403784 5.0

但其实这只是一种假象,Python函数返回的仍然是单一值——一个tuple

1
2
3
>>> r = cal(10, math.pi / 6)
>>> print(r)
(8.66025403784, 5.0)

在语法上,返回一个tuple可以省略括号,而多个变量可以同时接收一个tuple,按位置赋给对应的值,所以,Python的函数返回多值其实就是返回一个tuple,但写起来更方便。

递归函数

  1. 如果一个函数在内部调用自身本身,这个函数就是递归函数

  2. 递归函数的优点是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。

  3. 使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。尾递归可以有效防止栈溢出,但是Python并不支持:)

汉诺塔的移动可以看做是递归函数。我们对柱子编号为a, b, c,将所有圆盘从a移到c可以描述为:如果a只有一个圆盘,可以直接移动到c;如果a有N个圆盘,可以看成a有1个圆盘(底盘) + (N-1)个圆盘,首先需要把 (N-1) 个圆盘移动到 b,然后,将 a的最后一个圆盘移动到c,再将b的(N-1)个圆盘移动到c。请编写一个函数move(n, a, b, c),给定输入 n, a, b, c,打印出移动的步骤。
例如,输入 move(2, ‘A’, ‘B’, ‘C’),打印出:
A –> B
A –> C
B –> C

1
2
3
4
5
6
7
def move(n, a, b, c):
if n == 1:
print('%s-->%s' % (a, c))
else:
move(n-1, a, c, b)
move(1, a, b, c)
move(n-1, b, a, c)

函数的参数

定义默认参数

默认参数的作用是简化调用,你只要把必须的参数传进去。但在必要的时候,又可以传入额外的参数来覆盖默认参数值。默认参数必须指向不变对象

由于函数的参数按从左到右的顺序匹配,所以默认参数只能定义在必需参数的后面——def function(a, b=1, c=2)。b和c即是默认参数。

设置原则:当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。变化小的参数就可以作为默认参数。

定义可变参数

可变参数的目的也是为了简化调用。一个可变参数能让一个函数接受任意个参数

可变参数的名字前面有个*号,我们可以传入0个、1个或多个参数给可变参数。

1
2
3
4
5
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum

在函数内部,参数numbers接收到的是一个tuple

函数调用时,在list或者tuple前加一个*,可以把list或tuple的元素变成可变参数传进去。

1
2
3
>>> nums = [1, 2, 3]
>>> calc(*nums)
14

关键字参数

关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict

1
2
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)

1
2
>>> person('Adam', 45, gender='M', job='Engineer')
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}

和可变参数类似,也可以先组装出一个dict,然后,把该dict转换为关键字参数传进去:

1
2
3
>>> extra = {'city': 'Beijing', 'job': 'Engineer'}
>>> person('Jack', 24, **extra)
name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}

**extra表示把extra这个dict的所有key-value用关键字参数传入到函数的**kw参数,kw将获得一个dict,注意kw获得的dict是extra的一份拷贝,对kw的改动不会影响到函数外的extra

命名关键字参数

命名关键字参数用来限制传入函数的关键字参数的名字,只接收指定名字的参数

命名关键字参数需要一个特殊分隔符**后面的参数被视为命名关键字参数。命名关键字参数必须传入参数名

1
2
def person(name, age, *, city, job):
print(name, age, city, job)
1
2
>>> person('Jack', 24, city='Beijing', job='Engineer')
Jack 24 Beijing Engineer

如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了:

1
2
def person(name, age, *args, city, job):
print(name, age, args, city, job)

命名关键字参数可以有默认值,此时调用时可以不传入该参数:

1
2
def person(name, age, *, city='Beijing', job):
print(name, age, city, job)

参数组合

参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。

对于任意函数,都可以通过类似func(*args, **kw)的形式调用它,无论它的参数是如何定义的。

切片

对list/tuple进行切片

Python提供切片(Slice)操作符用于取list/tuple指定索引范围

1
>>> L = ['Adam', 'Lisa', 'Bart', 'Paul']

  • L[0:3]表示,从索引0开始取,直到索引3为止,但不包括索引3。即索引0,1,2。

    1
    2
    >>> L[0:3]
    ['Adam', 'Lisa', 'Bart']
  • 如果第一个索引是0,还可以省略:

    1
    2
    >>> L[:3]
    ['Adam', 'Lisa', 'Bart']
  • 只用一个:,表示从头到尾取全部元素(实际上复制出了一个新list。):

    1
    2
    >>> L[:]
    ['Adam', 'Lisa', 'Bart', 'Paul']
  • 切片操作还可以指定第三个参数。第三个参数表示每N个取一个,上面的 L[::2] 会每两个元素取出一个来,也就是隔一个取一个。

    1
    2
    >>> L[::2]
    ['Adam', 'Bart']

对list切片的结果是list,对tuple切片的结果是tuple。

倒序切片

倒序切片也是包含起始索引,不包含结束索引。

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> L = ['Adam', 'Lisa', 'Bart', 'Paul']

>>> L[-2:]
['Bart', 'Paul']

>>> L[:-2]
['Adam', 'Lisa']

>>> L[-3:-1]
['Lisa', 'Bart']

>>> L[-4:-1:2]
['Adam', 'Bart']

对字符串切片

字符串'xxx'可以看成是一种list,每个元素就是一个字符。因此,字符串也可以用切片操作,操作结果仍是字符串

1
2
3
4
5
6
>>> 'ABCDEFG'[:3]
'ABC'
>>> 'ABCDEFG'[-3:]
'EFG'
>>> 'ABCDEFG'[::2]
'ACEG'

迭代

什么是迭代

在Python中,迭代操作就是对于一个集合,无论该集合是有序还是无序,我们用 for 循环遍历这个集合,依次取出集合的每一个元素,这种遍历我们称为迭代(Iteration)。

注意:集合是指包含一组元素的数据结构,我们已经介绍的包括:

  1. 有序集合:list,tuple,str;
  2. 无序集合:set
  3. 无序集合并且具有 key-value 对:dict

Python的for循环抽象程度要高于Java的for循环。Python的for循环不仅可以用在listtuple上,还可以作用在其他可迭代对象上。

判断可迭代对象

通过collections模块的Iterable类型可以判断。

1
2
3
4
5
>>> from collections import Iterable
>>> isinstance([1,2,3], Iterable) # list可迭代
True
>>> isinstance(123, Iterable) # 整数不可迭代
False

迭代list的下标和元素

Python内置的enumerate函数可以把一个list变成“(索引, 元素)”形式的tuple组成的可迭代对象,这样就可以在for循环中同时迭代索引和元素本身:

1
2
3
4
5
6
>>> for i, value in enumerate(['A', 'B', 'C']):
... print(i, '-', value)
...
0 - A
1 - B
2 - C

迭代dict的value

dict 对象有一个values()方法,可以用来迭代dict的value,注意dict是无序的。

1
for v in d.values():

迭代dict的key

默认情况下,dict迭代的是key。

1
for key in d:

迭代dict的key和value

dict对象的items()方法可以用来同时迭代key和value。

1
2
3
4
5
6
7
>>> d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59 }
>>> for key, value in d.items():
... print('%s: %s' % (key, value))
...
Adam: 95
Bart: 59
Lisa: 85

列表生成式

生成列表

如果要生成[1x1, 2x2, 3x3, …, 10x10],方法一是循环,但是循环太繁琐;方法二是写列表生成式。

写列表生成式时,把要生成的元素 x * x 放到前面,后面跟 for 循环,就可以把list创建出来。

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

进行条件过滤

列表生成式的 for 循环后面还可以加上 if 判断,只有 if 判断为 True 的时候,才把循环的当前元素添加到列表中。

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

使用多层表达式

for循环可以嵌套,因此,在列表生成式中,也可以用多层 for 循环来生成列表。

1
2
>>> [m + n for m in 'ABC' for n in '123']
['A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3']

生成器

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器(Generator)。

使用()创建生成器

把一个列表生成式的[]改成(),就创建了一个generator。如果要一个一个打印出generator的元素,可以通过next()函数。generator保存的是算法,每次调用next(),就计算出下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。
当然,上面这种不断调用next()函数实在是太变态了,正确的方法是使用for循环,因为generator也是可迭代对象

1
2
3
4
5
6
7
8
9
10
11
>>> g = (x * x for x in range(2))
>>> g
<generator object <genexpr> at 0x1021e0a98>
>>> next(g)
0
>>> next(g)
1
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

使用yield创建生成器

  • 如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator。变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。同样的,把函数改成generator后,我们基本上从来不会用next()来调用它,而是直接使用for循环来迭代。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>>> def fib(max):
... n, a, b = 0, 0, 1
... while n < max:
... yield b
... a, b = b, a + b
... n = n + 1
...
>>> g = fib(6)
>>> g
<generator object fib at 0x101166a50>
>>> for i in g:
... print(i)
...
1
1
2
3
5
8

迭代器

可以被next()函数调用并不断返回下一个值的对象称为迭代器Iterator

通过collections模块的Iterator类型可以判断是否是迭代器。

1
2
3
4
5
>>> from collections import Iterator
>>> isinstance((x * x for x in range(10)), Iterator)
True
>>> isinstance('abc', Iterator)
False

迭代器和可迭代对象的区别

迭代器一定是可迭代对象,可迭代对象不一定是迭代器。两者是包含与被包含的关系。

生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator

Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。Iterator甚至可以表示一个无限大的数据流,例如全体自然数

把可迭代对象变为迭代器

把list、dict、str等Iterable变成Iterator可以使用iter()函数:

1
2
3
4
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True

Python的for循环本质上就是通过不断调用next()函数实现的。


写于2015年5月,2017年2月整理为Python3版本。

觉得有用可以请作者喝杯咖啡呀~
0%