文章目录
递归函数
>>> def fact(n):
... if n==1:
... return 1
... return n*fact(n-1)
...
>>> fact(4)
24
计算过程
===> fact(5)
===> 5 * fact(4)
===> 5 * (4 * fact(3))
===> 5 * (4 * (3 * fact(2)))
===> 5 * (4 * (3 * (2 * fact(1))))
===> 5 * (4 * (3 * (2 * 1)))
===> 5 * (4 * (3 * 2))
===> 5 * (4 * 6)
===> 5 * 24
===> 120
理论上,所有的递归函数都可以写成循环的方式,但循环逻辑不如递归。
要注意栈溢出
解决栈溢出的一个很好的办法是使用尾递归
尾递归是指:在函数返回的时候调用自身本身,并且,return语句不能包含表达式,
这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。
将上面递归改为尾递归代码如下
def fact(n):
return fact_iter(n, 1)
def fact_iter(num, product):
if num == 1:
return product
return fact_iter(num - 1, num * product)
遗憾的是,大部分编译器都没对尾递归做优化,python解释器也没有,所以任何递归函数都存在栈溢出的问题。
高级特性
切片
取一个list前三个元素
笨方法:
[L[0], L[1], L[2]]
取前n个时可以用for…in…循环
切片
[0:3]表示,从索引0开始取,直到索引3为止
如果第一个是0,还可以省略
L[:3]
L[-10:]表示最后面10个数
L[:10:2]表示取前十个,每两个取一个
L[::5]表示所有数每五个取一个
对tuple也可以切片操作
(0, 1, 2, 3, 4, 5)[:3]
(0, 1, 2)
字符串也可以切片操作
‘ABCDEFG’[:3]
‘ABC’
去除字符串中首尾的空格要借助str的strip函数实现
>>> a=' adf '
>>> a.strip()
'adf'
迭代
通过for循环来遍历list或tuple,这种遍历我们称为迭代
迭代dict
>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> for key in d:
... print(key)
...
a
c
b
迭代字符串
>>> for ch in 'ABC':
... print(ch)
...
A
B
C
输出value
>>> for i, value in enumerate(['A', 'B', 'C']):
... print(i, value)
...
0 A
1 B
2 C
输出成对的
>>> for x, y in [(1, 1), (2, 4), (3, 9)]:
... print(x, y)
...
1 1
2 4
3 9
列表生成式
要生成[11,22,33,…1010]怎么做
1)循环
>>> l=[]
>>> for x in range(1,11):
... l.append(x*x)
...
>>> l
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
但是太麻烦了
列表生成式可以用一行语句代替上面的list
>>> [x*x for x in range(1,11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
要把x*x放到前面(放在前面的是输出目标,后面哪个是x变量,后面的在变,前面给的也就会变喽),后面跟for循环
for循环后面还可以加上if判断,这样我们就可以筛选出仅偶数的平方:
>>> [x*x for x in range(1,11) if x%2==0]
[4, 16, 36, 64, 100]
还可以使用两层循环(类似于c语言中的for循环),可以生成全排列
>>> [m+n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
三层及以上也可以,只是用的比较少了
列表生成式 简单点的应用,列出当前文件下所有的文件和目录名
os是一个包
>>> import os
>>> [d for d in os.listdir('.')]
['DLLs', 'Doc', 'include', 'Lib', 'libs', 'LICENSE.txt', 'NEWS.txt', 'python.exe', 'python3.dll', 'python37.dll', 'pythonw.exe', 'Scripts', 'tcl', 'Tools', 'vcruntime140.dll']
for循环可以同时迭代多个变量
dict.items() 返回可遍历的(键,值)元组数组
>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> for k, v in d.items():
... print(k, '=', v)
...
y = B
x = A
z = C
列表生成式也可以使用两个变量来生成list
k+'='+v
此处加号相当于连接符吧
>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> [k + '=' + v for k, v in d.items()]
['y=B', 'x=A', 'z=C']
借助列表生成式来将list中所有的字符串变成小写
>>> L = ['Hello', 'World', 'IBM', 'Apple']
>>> [s.lower() for s in L]
['hello', 'world', 'ibm', 'apple']
生成器generator
一边循环一边计算的机制成为生成器
>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
创建一个generator
1)把列表生成式的[]改为()
创建一个list与generator的区别仅仅在于外面的[]和 (),L是一个list,g是一个generator
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x1022ef630>
如何打印出list的每一个元素
通过next()函数
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
以此类推
直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。
正确的方法是使用for循环,generator也是可迭代对象
>>> g = (x * x for x in range(10))
>>> for n in g:
... print(n)
...
0
1
4
9
16
25
36
49
64
81
我们创建一个generator后,基本上永远不会调用next(),而是通过for循环来迭代它,
如果推算的算法比较复杂,用类似列表生成器的for无法实现时,还可以借助函数
例如,斐波拉契数列,列表生成式是无法表示出来的,可以借助函数
函数写法
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print(b)
a, b = b, a + b
n = n + 1
return 'done'
注意赋值语句
a,b=b,a+b
相当于t = (b, a + b) # t是一个tuple
a = t[0]
b = t[1]
改为genenrator
将print(b)改为yield b 就可以了
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
把函数改成generator后,我们基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代:
普通函数调用和generator函数区别:
普通函数调用直接返回结果:
>>>r=abs(6)
>>>r
6
generator函数实际返回一个generator对象:
>>>g=fib(6)
>>>g
<generator object fib at 0x1022ef948>
迭代器Iterator
可以被next()函数调用并不断返回下一个值的对象称为迭代器Iterator
可以直接作用于for循环的数据类型有以下几种:
1)集合数据类型,list,tuple,dict,set,str等;
2)generator 包括生成器和带yeild的generator函数。
这些可以直接作用于for循环的对象统称为可迭代对象:Iterable对象;
可以用isinstance()判断一个对象是否为Inter able对象:
#先导入头文件!!!!
>>> 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
生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIterator错误后表示无法继续返回下一个值了
可用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
生成器都是generator对象 但list,dict,str虽然是Iterable,却不是Itertor
想把list,dict,str等Iterable变成Iterator可以使用iter()函数:
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True
解释iterator和iterable
这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。
Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
凡是可作用于for循环的对象都是Iterable类型;
凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。