学自廖雪峰老师的教程https://www.liaoxuefeng.com/wiki/1016959663602400 廖老师本就写的通
俗易懂,推荐时间充足的话直接看廖老师的教程。 本文是笔者在学习过程中的学习笔记,加了部分自己的理解
,更加简单易懂一些。也有部分是直接复制了廖老师的内容,若对本文内容有不解之处,请转至原网站进行查
看。
文章目录
python面向对象,解释型语言
python可以把任何数据都看成一个“对象”,也具有面向对象的特性
运行python程序是实时把源代码转换为二进制代码
初学python
同时输出字符串和整数,中间需要加逗号
同是字符串不需要
依次输出多个字符串,遇到逗号当作空格输出
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-si5OjJXK-1608126012547)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20201207190437605.png)]
加上引号当字符串为处理不运算
python 基础
-
行首加#表示注释
-
缩进约定成俗是四个空格
-
python对大小写敏感
数据类型
可变 不可变
不可变类型:整数、浮点型、字符串、元组,空值
对于不变对象来说,调用对象自身的任意方法,也不会改变该对象自身的内容。相反,这些方法会创建新的对象并返回,这样,就保证了不可变对象本身永远是不可变的.
>>> a = 'abc'
>>> b = a.replace('a', 'A')
>>> b
'Abc'
>>> a
'abc'
当我们调用a.replace(‘a’, ‘A’)时,实际上调用方法replace是作用在字符串对象’abc’上的,而这个方法虽然名字叫replace,但却没有改变字符串’abc’的内容。相反,replace方法创建了一个新字符串’Abc’并返回赋值给了b
可变类型:列表、字典、集合
整数
-
任意整数,没有大小限制
包括负整数 可以直接写成-100
-
允许在数字中间加_进行分隔 例如:-1000000和-100_00_00一样 为了方便读
-
整数计算在计算机内部存储永远都是精确的 除法也不例外
#### 除法
- /结果是浮点数 即使除数和被除数都是整数结果也是浮点数
- //地板除 结果只保留整数
- %取余 结果也是整数
浮点数
- 也就是小数
- 正常的数可以直接写1.23,过大或过小的数要用科学计数法 10用e表示 例如:0.000001 表示为1e-6
- 存储时会有四舍五入的误差
- python的浮点数也没有大小限制,但是超出一定范围就直接表示为
inf
(无限大)
### 布尔值
- True和False两个值
- 布尔值可以用与或非的运算 and or not (not 是单目运算符 加在布尔值前)
### 空值
- None python一个特殊的值
- None和0不一样 0是有意义的
### 字符串
-
以单引号或双引号或三引号括起来的任意文本
-
当你用单引号’ '定义字符串的时候,它就会认为你字符串里面的双引号" "是普通字符,从而不需要转义。反之当你用双引号定义字符串的时候,就会认为你字符串里面的单引号是普通字符无需转义
-
三引号
用
'''...'''
的格式表示多行内容>>> print('''line1 ... line2 ... line3''') line1 line2 line3
输入
print(```文本
然后回车会出现...
的提示符就是换行了 写完的时候加上```和右括号
-
-
字符串内部转义字符\
\t 制表符 \n换行符 想输出\本身也需要转义
-
r’‘表示’'内部的字符串不转义
>>> print('\\\t\\') \ \ >>> print(r'\\\t\\') \\\t\\
编码问题
字符“0”和整数0对应的编码不同
ASCII
编码:
- 127个字符(大小写英文字母 数字 符号)
- 用一个字节表示
GB2312
编码:中国制定 加入了中文字符
。。。。等等 各国都有自己的编码方式(也就容易导致乱码)
Unicode
编码:
- 把所有语言统一起来
- 通常用两个字节表示一个字符 (特殊情况也会有四个字节表示一个复杂的字符)
- ASCII码表示一个字符 再拿Unicode进行编码 等同在ASCII的编码前八位补0
UTF-8
编码:
- 出现契机:有些字符可以用ASCII码表示,但是为了统一都用Unicode码表示了 这样就会浪费一部分空间,所以出现了可变长编码的UTF-8编码
- UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节
我的理解就是把一长串字符根据所需存储空间的大小分成几类字符,然后根据不同的类去编码为不同长度的字节。
不同编码之间的转变目的就是为了节约内存 加快传输
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7s1GB40X-1608126012548)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20201209001119971.png)]
python中的字符串
-
python3 Unicode编码 ——>python支持多语言
-
提供了编码和字符转换的函数
ord()
字符——>整数编码chr()
编码——>字符 -
python的字符串类型是str 内存中用unicode表示
len(str)
用来计算str的长度【单位是字符】 -
字符串上传到网络或保存到磁盘,需要把str变为以字节为单位的bytes
bytes类型的数据用b带前缀加引号表示
x = b'ABC'
在bytes中,无法显示为ASCII字符的字节,用\x##显示
len(b'')
用来计算字节数>>> len(b'ABC') 3 >>> len(b'\xe4\xb8\xad\xe6\x96\x87') 6 >>> len('中文'.encode('utf-8')) 6
-
str——>bytes
encode()
-
bytes——>str
decode()
>>>b'\xe4\xb8\xad\xff'.decode('utf-8', errors='ignore') '中'
errors="ignore"
作为decode的一个参数,用于忽略bytes到str转换过程中无效的字节
-
-
字符串格式化
- 和c语言差不多也是用%实现 %?占位符
常见的占位符有:
占位符 替换内容 %d 整数 %f 浮点数 %s 字符串 %x 十六进制整数 print('%2d-%02d' % (3, 1)) print('%.2f' % 3.1415926) 3-01 3.14
字符串中包含%是一个普通字符,用%%表示,也相当于转义
-
format()
用传入的参数依次替换字符串内的占位符{0}、{1}……
>>> 'Hello, {0}, 成绩提升了 {1:.1f}%'.format('小明', 17.125) 'Hello, 小明, 成绩提升了 17.1%'
-
f-string
f+字符串 字符串中包含{}中的变量则发生替换>>> r = 2.5 >>> s = 3.14 * r ** 2 >>> print(f'The area of a circle with radius {r} is {s:.2f}') The area of a circle with radius 2.5 is 19.62
list
-
列表,一种有序的集合,可变
-
len()
可用来求列表元素的个数 -
用索引获取不同位置的元素,类似于C语言的数组
-
获取倒数的元素,可以用复数 例如 索引为-1是最后一个元素
-
append()
在list末尾增加元素insert(index,要添加的元素)
在指定位置添加元素 -
pop()
删除末尾的元素pop(index)
删除指定位置元素 -
替换 直接赋值给想要替换的索引位置
-
一个list 可以包含不同的数据类型
甚至可以包括list 也就是二维数组
列表名[index1][index2]
双下标获取元素 -
可以创建空列表 长度为0
tuple
-
元组,有序列表
-
一旦初始化不能修改
-
正因为不可变所以没有插入,添加的方法 也不能直接赋值修改元素
-
tuple(1)和tuple(1,)
tuple(1)会被认为是数学计算意义的括号 其最终结果是1这个数
tuple(1,)结果是(1,)数组
>>> t = (1) >>> t 1
>>> t = (1,)
>>> t
(1,)
-
t=tuple()
创建空元组 -
元组也不是绝对的不可变
元组的指向不变,但是内容未必不能变
比如元组的一个元素是list,元组指向的list不可变,但是list的内容是可变的,所以看起来元组的内容也是可变的
所以要创建一个内容也不能变的元组必须保证元组的每一个元素也不可变
dict
-
dict, dictionary,字典 类似Java的map 键-值(key-value)存储 key不可变
dict内部存放的顺序和key放入的顺序是没有关系
例子
>>> d = {'Michael': 95, 'Bob': 75, 'Tracy': 85} >>> d['Michael'] 95
-
增加dic的元素 ,通过key放入,一个key只能放一个value,多次赋值会发生覆盖,最后一次赋值是key的最终值
>>> d['Adam'] = 67 >>> d['Adam'] 67
-
查找dic的元素
-
查找一个不存在的key 会报错
-
判断dic中是否有某个key
-
key in dic
判断dic是否有这个key 结果是布尔值 -
get()
key不存在返回None 或返回指定的值>>> d.get('Thomas') >>> d.get('Thomas', -1) -1
注意:返回
None
的时候Python的交互环境不显示结果 -
-
-
删除dic的元素
pop(key)
key-value都会被删除 -
dic和list相比
dic的优势:查找和插入的速度极快,不会随着key的增加而变慢;
劣势:需要占用大量的内存,内存浪费多。
空间换时间
list的优势:查找和插入的时间随着元素的增加而增加;
劣势:占用空间小,浪费内存很少。
set
-
存储key的集合,不储存value. 只能存不可变的对象
-
要创建一个set,需要提供一个list作为输入集合:
>>> s = set([1, 2, 3]) >>> s {1, 2, 3}
传入的参数是一个list,但是不代表set包含的元素有list,实际上set包含的是1,2,3这3个元素
-
set是一个
无序
无重复
的集合初始化一个set时,如果元素有重复会被自动过滤
可以做交集,并集
>>> s1 = set([1, 2, 3]) >>> s2 = set([2, 3, 4]) >>> s1 & s2 {2, 3} >>> s1 | s2 {1, 2, 3, 4}
-
add(key)
添加元素 -
remove(key)
删除元素
变量
-
变量可以表示任意数据类型
变量就是用来指向各种数据对象的,对变量赋值就是把数据和变量关联起来
-
变量名必须是英文,数字,下划线_
不能用数字开头
-
等号=是赋值语句 可以把任意类型的值赋值给变量 同一个变量可以反复赋值,可以是不同类型的值
-
动态语言
和静态语言
动态语言:变量本身类型不固定的语言 比如python 可以反复赋不同类型的值
动态语言:定义变量时必须指定变量类型的语言 比如Java int a; 不能再把非整型的值赋给a
-
-
=和==不一样 =不是数学意义上的等于 ==更像是数学上的=
-
变量在计算机内存中的表示
a="abc"
这个语句,python解释器做了两个动作 一是创建了一个"abc"的字符串 二是创建了a变量 a指向字符串abc
a=b
这个语句的意思是把变量a指向的值赋值给变量b
第二句把a的值赋值给b b和a就没关系了 之后a指向新的数据 b不变
和c语言的指针的不一样
常量
-
在Python中,通常用全部大写的变量名表示常量
-
python中常量的值也可以改变 python中没有强制的类型声明 所以所谓常量被重新赋值了也不会报错
条件判断
-
age = 3 if age >= 18: print('adult') elif age >= 6: //else if print('teenager') else: print('kid')
没有C语言的花括号,通过缩进区分
-
if是从上往下判断,一旦满足条件,不会继续判断之后的条件(也就是说即使同时满足两个条件,只会执行第一个条件之后的代码)
-
if 可简写
if x: print('True')
只要x非0,非空(空列表 空字符串),都当是true
输入
-
input()
可以用来读取用户输入 -
input()返回的数据类型是str,str不能直接和整数比较,必须先把str转换成整数。
Python提供了
int()
函数来完成这件事情int()
函数发现一个字符串并不是合法的数字时就会报错(去了字符串的引号不是数字)
s = input('birth: ') //input('提示用户输入的内容')
birth = int(s)
if birth < 2000:
print('00前')
else:
print('00后')
循环
-
第一种 for x in list/tuple
把每个元素代入变量x,然后执行缩进块的语句。
names = ['Michael', 'Bob', 'Tracy'] for name in names: print(name)
执行这段代码,会依次打印names的每一个元素:
Michael Bob Tracy
再比如我们想计算1-10的整数之和,可以用一个sum变量做累加:
sum = 0 for x in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]: sum = sum + x print(sum)
range()
生成一个整数序列>>> list(range(5)) [0, 1, 2, 3, 4]
-
第二种 while循环,只要条件满足,就不断循环,条件不满足时退出循环
比如我们要计算100以内所有奇数之和,可以用while循环实现:
sum = 0 n = 99 while n > 0: sum = sum + n n = n - 2 print(sum)
在循环内部变量
n
不断自减,直到变为-1
时,不再满足while条件,循环退出### break
提前退出循环
continue
跳过当前的这次循环,直接开始下一次循环
如果我们想只打印奇数,可以用
continue
语句跳过某些循环:n = 0 while n < 10: n = n + 1 if n % 2 == 0: # 如果n是偶数,执行continue语句 continue # continue语句会直接继续下一轮循环,后续的print()语句不会执行 print(n)
函数
调用函数
-
abs()
求绝对值的函数 只有一个参数 且是整数或浮点数 -
max()
求最大值函数 可接受多个参数 返回最大值 -
数据类型转换函数
-
int()
把其他数据类型转换为整数 -
float()
把其他数据类型转换为浮点数 -
str()
把其他数据类型转换为字符串 -
hex()
把一个整数转换成十六进制表示的字符串 -
bool()
把其他数据类型转换为布尔值>>>bool('') //空的布尔值就是False >>>False >>>bool('sadasd') //只要不空就是True >>>True >>>bool(1) >>>True >>>bool(0) >>>False
-
-
把函数赋给一个变量 (函数名就是指向一个函数对象,把函数赋给变量 相当于给函数又了个名字,之后可以通过新名字使用这个函数)
>>> a = abs # 变量a指向abs函数 >>> a(-1) # 所以也可以通过a调用abs函数 1
定义函数
在Python中,定义一个函数要使用def
语句,依次写出函数名、括号、括号中的参数和冒号:
,然后,在缩进块中编写函数体,函数的返回值用return
语句返回。
def my_abs(x):
if x >= 0:
return x
else:
return -x
函数体内部的语句在执行时,一旦执行到return
时,函数就执行完毕,并将结果返回
如果没有return
语句,函数执行完毕后也会返回结果,只是结果为None
。return None
可以简写为return
- 空函数
功能块语句光写pass
语句[占位符的功能]
def nop():
pass
-
参数检查
自己写的函数有些时候参数传递错误,但是python解释器检查不出错误
需要自己写错误判断和处理(类似于java的try…catch…)
def my_abs(x): if not isinstance(x, (int, float))://如果出现传入的参数不是整数或浮点数就会报这个错 raise TypeError('bad operand type') if x >= 0: return x else: return -x
-
返回多个值【实质是返回一个元组 只不过元组的括号可以省略】
## 函数的参数
位置参数
调用函数时,传入的值按照位置顺序依次赋给参数
函数定义的时候有几个参数,调用的时候就必须传入几个值
默认参数
-
必选参数在前,默认参数在后
-
当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。变化小的参数就可以作为默认参数
-
好处:降低调用函数的难度 (有默认值的参数可以不进行传值)
-
函数调用的时候,1.按顺序传值 2.参数名=‘’
-
默认参数必须指向不变对象
例如:默认参数指向list的时候,list的值变了的话,默认参数就变了导致函数默认值发生变化
可变参数
-
习惯写法:
*args
是可变参数,args接收的是一个tuple;可变参数既可以直接传入:
func(1, 2, 3)
,又可以先组装list或tuple,再通过*args
传入:func(*(1, 2, 3))
; -
可变参数就是传入的参数个数是可变的
-
函数定义时参数前加一个* 函数调用时自动组装为一个tuple
def calc(*numbers): sum = 0 for n in numbers: sum = sum + n * n return sum
>>> calc(1, 2) 5 >>> calc() 0
-
如果传入的参数本身就是一个集合 在其前加* 相当于依次传入集合的所有元素
关键字参数
-
习惯写法:
**kw
是关键字参数,kw接收的是一个dict;关键字参数既可以直接传入:
func(a=1, b=2)
,又可以先组装dict,再通过**kw
传入:func(**{'a': 1, 'b': 2})
-
也是允许传入任意个参数,调用时组装成一个dic
-
好处:扩展函数功能
-
具体用法
def person(name, age, **kw): print('name:', name, 'age:', age, 'other:', kw)
>>> person('Adam', 45, gender='M', job='Engineer') name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
>>> extra = {'city': 'Beijing', 'job': 'Engineer'} >>> person('Jack', 24, **extra) name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
**extra 表示把extra 这个dic所有的key-value用关键字参数传入到 **kw 参数 kw获得的只是extra的值
kw变化 不会影响extra
-
命名关键字函数
限制关键字参数的名字
需要加在参数前加* ,不加*解释器无法识别位置参数和关键字参数
# city job就是关键字参数 调用时候需要输入 def person(name, age, *, city, job): print(name, age, city, job)
参数组合
参数的类型有多种,可以组合使用但是必须要按照顺序
必选参数、默认参数、可变参数、命名关键字参数和关键字参数
在函数调用的时候,Python解释器自动按照参数位置和参数名把对应的参数传进去。
def f1(a, b, c=0, *args, **kw):
print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
def f2(a, b, c=0, *, d, **kw):
print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)
>>> f1(1, 2)
a = 1 b = 2 c = 0 args = () kw = {}
>>> f1(1, 2, c=3)
a = 1 b = 2 c = 3 args = () kw = {}
>>> f1(1, 2, 3, 'a', 'b')
a = 1 b = 2 c = 3 args = ('a', 'b') kw = {}
>>> f1(1, 2, 3, 'a', 'b', x=99)
a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}
>>> f2(1, 2, d=99, ext=None)
a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}
>>> args = (1, 2, 3, 4)
>>> kw = {'d': 99, 'x': '#'}
# a b c 是必选的 所以传入的args相当于被拆开了,得先把abc赋了值,剩下的给args
>>> f1(*args, **kw)
a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'}
>>> args = (1, 2, 3)
>>> kw = {'d': 88, 'x': '#'}
>>> f2(*args, **kw)
a = 1 b = 2 c = 3 d = 88 kw = {'x': '#'}
递归函数
- 啥是递归函数?
如果函数在内部调用该函数本身,这个函数就是递归函数。
-
所有的递归函数都可以写成循环的方式。
-
缺点:递归调用的次数过多,会导致栈溢出
在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。
【栈帧这个东西前几天听C语言讲课刚知道,当时在C语言的场景中讲的是malloc函数分配内存,一个函数调用的时候会开辟一个栈帧(不知道该用什么量词,反正就是在内存中开辟一块地方来运行这个函数),函数结束时候栈帧会被释放,但是由于malloc声明的内存不在函数所在的栈帧中,所以函数内存被释放时 malloc创建的空间还在】
解决栈溢出的方法:尾递归
尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。
循环就是一种特殊的尾递归函数。
不过,多数编程语言没有针对尾递归做优化(简单说就是有解决栈溢出的方法,但是没做,python解释器也没做优化)
高级特性
这些高级特性就是为了提高开发效率,越简单越好
切片(slice)
>>> L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']
>>> L[0:3]
['Michael', 'Sarah', 'Tracy']
L[0:3]
取了L列表的前三个元素(也就是下标0,1, 2的元素)
L[:3]
也是前三个 省略了0
L[1:3]
取下标1, 2的元素
针对下标[]左闭右开
倒数切片
L[-1]
取倒数第一个元素
L[-2:]
倒数后两个元素
设置间隔
L[:10:2]
前10个数,每两个取一个
L[::5]
所有数,每5个取一个
复制整个list
L[:]
相当于从头切到尾
tuple也可以切片
tuple也是一种list,唯一区别是tuple不可变。因此,tuple也可以用切片操作,只是操作的结果仍是tuple:
>>> (0, 1, 2, 3, 4, 5)[:3]
(0, 1, 2)
字符串也可以切
字符串'xxx'
也可以看成是一种list,每个元素就是一个字符。因此,字符串也可以用切片操作,只是操作结果仍是字符串:
>>> 'ABCDEFG'[:3]
'ABC'
>>> 'ABCDEFG'[::2]
'ACEG'
迭代(Iteration)
用for循环遍历list或tuple就是迭代
for...in
不仅仅是list,tuple,只要是可迭代对象,无论有无下标,都可以迭代.比如dic
-
dic迭代
-
因为dict的存储不是按照list的方式顺序排列,所以,迭代出的结果顺序很可能不一样
-
默认情况下,dict迭代的是key
-
如果要迭代value,可以用
for value in d.values()
-
如果要同时迭代key和value,可以用
for k, v in d.items()
-
-
字符串迭代
>>> for ch in 'ABC': ... print(ch) ... A B C
判断一个对象是否是可迭代对象
通过collections模块的Iterable类型判断:
>>> from collections import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代
True
>>> isinstance([1,2,3], Iterable) # list是否可迭代
True
>>> isinstance(123, Iterable) # 整数是否可迭代
False
isinstance() 函数来判断一个对象是否是一个已知的类型,类似 type()。
实现list下标循环
Python内置的enumerate
函数可以把一个list变成索引-元素对,这样就可以在for
循环中同时迭代索引和元素本身:
>>> for i, value in enumerate(['A', 'B', 'C']):
... print(i, value)
...
0 A
1 B
2 C
enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标
列表生成式(List Comprehensions)
Python内置的,用来创建list的生成式
如果要生成[1x1, 2x2, 3x3, ..., 10x10]
循环做法:
>>> L = []
>>> for x in range(1, 11):
... L.append(x * x)
...
>>> L
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
列表生成式简化过后:
>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
for循环后面还可以加上if判断,这样我们就可以筛选出仅偶数的平方:
>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]
在一个列表生成式中,for
前面的if ... else
是表达式,而for
后面的if
是过滤条件,不能带else
。(在列表生成式中想要加判断条件,写在前边必须加else,写在后边不能加else)
还可以使用两层循环,可以生成全排列:
>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
for
循环其实可以同时使用两个甚至多个变量,比如dict
的items()
可以同时迭代key和value:
>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> for k, v in d.items():
... print(k, '=', v)
...
y = B
x = A
z = C
列表生成式也可以使用两个变量来生成list:
>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> [k + '=' + v for k, v in d.items()]
['y=B', 'x=A', 'z=C']
生成器(generator)
在Python中,这种一边循环一边计算的机制,称为生成器:generator。
如何创建一个生成器
-
把一个列表生成式的
[]
改成()
>>> 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 <generator object <genexpr> at 0x1022ef630>
generator函数的“调用”实际返回一个generator对象
创建
L
和g
的区别仅在于最外层的[]
和()
,L
是一个list,而g
是一个generator。>>> next(g) 0 >>> next(g) 1 >>> next(g) 4 >>> next(g) 9 >>> next(g) 16 >>> next(g) 25 >>> next(g) 36 >>> next(g) 49 >>> next(g) 64 >>> next(g) 81 >>> next(g) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
每次调用
next(g)
,就计算出g
的下一个元素的值。不过这样做太笨了。generator也是可迭代对象,可以使用
for
循环。>>> g = (x * x for x in range(10)) >>> for n in g: ... print(n) ... 0 1 4 9 16 25 36 49 64 81
-
如果一个函数定义中包含
yield
关键字,那么这个函数就不再是一个普通函数,而是一个generatorgenerator和函数的执行流程不一样。函数是顺序执行,遇到
return
语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()
的时候执行,遇到yield
语句返回,再次执行时从上次返回的yield
语句处继续执行**(感觉有点像C语言的break和continue的结合,碰到yield结束函数,下次运行接着上次结束的位置)**
def odd():
print('step 1')
yield 1
print('step 2')
yield(3)
print('step 3')
yield(5)
>>> o = odd() //生成一个generator对象
>>> next(o)//用next()函数获得下一个返回值
step 1
1
>>> next(o)
step 2
3
>>> next(o)
step 3
5
>>> next(o)//拿不到返回值报错
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
如果想要拿到返回值,必须捕获StopIteration
错误,返回值包含在StopIteration
的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
循环的数据类型
- 一类是集合数据类型,如
list
、tuple
、dict
、set
、str
等; - 一类是
generator
,包括生成器和带yield
的generator function
可以直接作用于for
循环的对象统称为可迭代对象:Iterable
#判断一个对象是否是可迭代对象
>>> from collections.abc import Iterable
>>> isinstance([], Iterable)
True
啥是迭代器
可以被next()
函数调用并不断返回下一个值的对象称为迭代器:Iterator
。
Iterator
和Iterable
Iterator
迭代器(可以被next()函数调用并不断返回下一个值的对象称为迭代器)
Iterable
可迭代对象(直接作用于for循环的对象)
生成器都是Iterator
对象,但list
、dict
、str
虽然是Iterable
,却不是Iterator
。
为啥???
这是因为Python的`Iterator`对象表示的是一个数据流,Iterator对象可以被`next()`函数调用并不断返回下一个数据,直到没有数据时抛出`StopIteration`错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过`next()`函数实现按需计算下一个数据,所以`Iterator`的计算是惰性的,只有在需要返回下一个数据时它才会计算。
`Iterator`甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
把list
、dict
、str
等Iterable
变成Iterator
可以使用iter()
函数:
>>> isinstance([], Iterator)
False
>>> isinstance(iter([]), Iterator)
True
凡是可作用于for
循环的对象都是Iterable
类型;
凡是可作用于for
循环的对象都是Iterable
类型;
凡是可作用于for
循环的对象都是Iterable
类型;
凡是可作用于for
循环的对象都是Iterable
类型;
之后还有
直接在廖老师的网站学习吧
好吧是因为笔者复习别的科目来不及写了