python之推导式 生成器 生成器函数 递归函数

推导式

通过一行循环判断遍历出一些列数据的方法叫做推导式

语法:
  val for val in iterable

 

1.推导式基本语法

lst = []  for  i  in  range(1,51 ):
lst.append(i)
print (lst)

改写推导式

lst = [  i  for  i  in  range(1,51 )  ]  print (lst)

小练习

 #  1.[1,2,3,4,5] => [2,4,6,8,10] 
lst = [ i*2  for  i  in  range(1,6 ) ]  print (lst)

 

2.带有判断条件的推导式

注意点:for后面紧跟的判断条件只能是单项分支.
[1,2,3,4,5,6,7,8,9,10] => [1,3,5,7,9 ... ]

lst = [1,2,3,4,5,6,7,8,9,10 ]
lst_new
= [] for i in lst: if i % 2 == 1 :
lst_new.append(i)
print (lst_new)

改写推导式

lst = [ i  for  i  in  lst  if  i % 2 == 1 ]  print (lst)

 

3.多循环推导式 # 谁♡♢♤♠谁

lst1 = [ "  孙杰龙  " , "  陈露  " , "  曹静怡  "  ]
lst2
= [ " 王志国 " , " 邓鹏 " , " 合理 " ]
lst_new
= [] for i in lst1: for j in lst2:
lst_new.append(i
+ " ♡♢♤♠ " + j) print (lst_new)

改写推导式

lst = [ i+ "  ♡♢♤♠  " +j  for  i  in  lst1  for  j  in  lst2 ]  print (lst

 

4.带有判断条件的多循环推导式

lst_new = []  for  i  in  lst1:  for  j  in  lst2:  if  lst1.index(i) == lst2.index(j):
lst_new.append(i
+ " ♡♢♤♠ " + j) print (lst_new)

改写推导式

lst = [i+ "  ♡♢♤♠  " +j  for  i  in  lst1  for  j  in  lst2  if  lst1.index(i) == lst2.index(j)]  print (lst)

 

集合推导式

案例:
  满足年龄在18到21,存款大于等于5000 小于等于5500的人,
  开卡格式为:尊贵VIP卡老x(姓氏),否则开卡格式为:抠脚大汉卡老x(姓氏)
  把开卡的种类统计出来

lst = [
{
" name " : " 赵沈阳 " , " age " :18, " money " :3000 },
{
" name " : " 赵万里 " , " age " :19, " money " :5200 },
{
" name " : " 赵蜂拥 " , " age " :20, " money " :100000 },
{
" name " : " 赵世超 " , " age " :21, " money " :1000 },
{
" name " : " 王志国 " , " age " :18, " money " :5500 },
{
" name " : " 王永飞 " , " age " :99, " money " :5500 }
]
setvar
= set() for i in lst: print (i) # {'name': '赵沈阳', 'age': 18, 'money': 3000} if 18 <= i[ " age " ] <= 21 and 5000 <= i[ " money " ] <= 5500 :
res
= " 尊贵VIP卡老{} " .format(i[ " name " ][0]) else :
res
= " 抠脚大汉卡老{} " .format(i[ " name " ][0]) # 添加到集合中 setvar.add(res) print (setvar)

{ 三元运算符 + 推导式 }

setvar = {  "  尊贵VIP卡老{}  " .format(i[ "  name  " ][0])  if  18 <= i[ "  age  " ] <= 21  and  5000 <= i[ "  money  " ] <= 5500  else   "  抠脚大汉卡老{}  " .format(i[ "  name  " ][0])  for  i  in  lst }  print (setvar)

 

字典推导式

一.enumerate

enumerate(iterable,[start=0])
功能:枚举 ; 将索引号和iterable中的值,一个一个拿出来配对组成元组,通过迭代器返回
参数:
  iterable: 可迭代性数据 (常用:迭代器,容器类型数据,可迭代对象range)
  start: 可以选择开始的索引号(默认从0开始索引)
返回值:迭代器

 

基本语法

 from  collections  import  Iterator,Iterable
lst
=[ " 王文 " , " 吕洞宾 " , " 何仙姑 " , " 铁拐李 " , " 张国老 " , " 曹国舅 " , " 蓝采和 " , " 韩湘子 " ]
it
= enumerate(lst)
it
= enumerate(lst,start=100 ) print (isinstance(it,Iterator))

next

 print ( next(it) )

for + next (推荐,数据较大时使用)

 for  i  in  range(3 ):  print (next(it))

for

 for  i  in  it:  print (i)

list 强转迭代器

 print (list(it))

 

(1) 字典推导式 配合 enumerate 来实现

dic = {k:v  for  k,v  in  enumerate(lst,start=100 )}  print  (dic)  """  (100, '王文')
(101, '吕洞宾')
(102, '何仙姑')
(103, '铁拐李')
(104, '张国老')
(105, '曹国舅')
(106, '蓝采和')
(107, '韩湘子')
"""

(2) 使用dict强转迭代器,瞬间得到字典

dic = dict( enumerate(lst,start=100 ) )  print (dic)

 

二.zip

特点:按照索引配对
zip(iterable, ... ...)
  功能: 将多个iterable中的值,一个一个拿出来配对组成元组,通过迭代器返回
  iterable: 可迭代性数据 (常用:迭代器,容器类型数据,可迭代对象range)
返回: 迭代器

 

基本语法

 #  lst1 = ["孙开启","王永飞","于朝志"]  #  lst2 = ["薛宇健","韩瑞晓","上朝气"]  #  lst3 = ["刘文博","历史园","张光旭"] 

 #  在索引下标同时存在时,才会进行配对,否则舍弃. 
lst1 = [ "  孙开启  " , "  王永飞  " , "  于朝志  "  ]
lst2
= [ " 薛宇健 " , " 韩瑞晓 " ]
lst3
= [ " 刘文博 " ]

it
= zip(lst1,lst2,lst3) print (list(it))

 

(1) 字典推导式 配合 zip 来实现

lst_key = [ "  ww  " , "  axd  " , "  yyt  "  ]
lst_val
= [ " 王维 " , " 安晓东 " , " 杨元涛 " ] # ('ww', '王维'), ('axd', '安晓东'), ('yyt', '杨元涛') dic = {k:v for k,v in zip(lst_key , lst_val) } print (dic)

(2) 使用dict强转迭代器,瞬间得到字典

dic = dict( zip(lst_key , lst_val) )  print (dic)

 

 生成器

生成器本质是迭代器,允许自定义逻辑的迭代器

迭代器和生成器区别:

  迭代器本身是系统内置的.重写不了.
  而生成器是用户自定义的,可以重写迭代逻辑

生成器可以用两种方式创建:
  (1)生成器表达式 (里面是推导式,外面用圆括号)
  (2)生成器函数 (用def定义,里面含有yield)

 

(1) 生成器表达式 (里面是推导式,外面用圆括号)

gen = ( i  for  i  in  range(10 ) )  print (gen)

 

判断类型

 from  collections  import  Iterator,Iterable  print (isinstance(gen,Iterator))

1.next 调用生成器

 print  (next(gen))  print (next(gen))

2.for + next 调用生成器

 for  i  in  range(3 ):  print (next(gen))

3.for 调用生成器所有数据

 for  i  in  gen:  print (i)

4.list强转生成器,瞬间得到所有数据

gen = ( i  for  i  in  range(10 ) )  print  (list(gen))  #  print(next(gen)) error  # StopIteration 

 

 

生成器函数

yield 类似于 return
共同点 在于:执行到这句话都会把值返回出去
不同点 在于:yield每次返回时,会记住上次离开时执行的位置 , 下次在调用生成器 , 会从上次执行的位置往下走,而return直接终止函数,每次重头调用.
yield 6 和 yield(6) 2种写法都可以 yield 6 更像 return 6 的写法 推荐使用

 

(1) 基本语法

 def  mygen():  print ( "  111  "  )  yield  1
    
     print ( "  222  "  )  yield  2
    
     print ( "  333  "  )  yield  3
   
 #  初始化生成器函数  => 返回生成器对象 => 简称生成器 
gen = mygen()  #  第一次调用 
res = next(gen)  print  (res)  #  第二次调用 
res = next(gen)  print  (res)  #  第三次调用 
res = next(gen)  print  (res)  #  第四次调用 
 """  StopIteration error 
res = next(gen)
print(res)
""" """ # 第一次调用
print("111") yield 1 保存当前第13行代码的状态,把1返回,并且等待下一次调用
# 第二次调用
从上一次保存的位置13行往下走, print("222") yield 2 保存当前第16行代码的状态,把2返回,并且等待下一次调用
# 第三次调用
从上一次保存的位置16行往下走, print("333") yield 3 保存当前第19行代码的状态,把3返回,并且等待下一次调用
# 第四次调用
因为没

 

(2) 优化生成器代码

生成器应用的场景是在大数据的范围中使用,切记不可直接用for遍历所有,可能无法短时间内获取所有数据

 def  mygen():  for  i  in  range(1,101 ):  yield  i  #  初始化生成器函数 => 生成器 
gen = mygen()  print ( "  <=====>  "  )  for  i  in  range(30 ):
num
= next(gen) print ( " 我的球衣号码是{} " .format(num)) print ( " <=====> " ) for i in range(40 ):
num
= next(gen) print ( " 我的球衣号码是{} " .format(num))

 

(3) send的使用方式 (给上一个yield发送数据)

next和send区别:
  next 只能取值
  send 不但能取值,还能发送值
send注意点:
  第一个 send 不能给 yield 传值 默认只能写None
  最后一个yield 接受不到send的发送值

 def  mygen():  print ( "  start  "  )

res
= yield " 内部1 " print (res, " <==内部==> " )

res
= yield " 内部2 " print (res, " <==内部==> " )

res
= yield " 内部3 " print (res, " <==内部==> " ) print ( " end " ) # 初始化生成器函数 => 生成器 gen = mygen() # 第一次调用生成器 """ 第一次调用生成器时,因为没有遇到yield保存的代码位置,
无法发送数据,默认第一次只能发送None
""" res = gen.send(None) print (res, " <==外部==> " ) # 第二次调用生成器 res = gen.send( " 100 " ) print (res, " <==外部==> " ) # 第三次调用生成器 res = gen.send( " 200 " ) print (res, " <==外部==> " ) # 第四次调用生成器 """ error
res = gen.send("300")
print(res,"<==外部==>")
""" """ 使用send调用生成器,第一次发送时必须是None,因为还没有遇到yield保存的代码位置
res = gen.send(None) 走到mygen生成器函数中
print("start")
res = yield "内部1" 执行第80行 ,保存退出,记录当前代码位置,将 "内部1" 返回
在98行接受数据 res = "内部1" print(内部1,"<==外部==>")

第二次调用生成器
res = gen.send("100") 把100这个数据发送给上一次代码保存的位置80行进行接受. => 导致 80行 res = 100
打印81行 print(100 ,"<==内部==>")
执行83行 res = yield "内部2" 保存退出,记录当前代码位置,将 "内部2" 返回
执行102行 res = gen.send("100") => "内部2" print("内部2","<==外部==>")

....
依次类推 ...
到第四次调用时, 因为没有更多的yield 返回数据,gen.send(300)无法接受到返回值,所以出现停止迭代 StopIteration的报错,程序终止;
"""

 

(4) yield from 的使用

将一个可迭代对象变成一个迭代器返回

 def  mygen():
lst
= [ " 张磊 " , " 李亚峰 " , " 刘一峰 " , " 王同培 " ] yield from lst # 初始化生成器函数 gen = mygen() print (next(gen)) print (next(gen)) print (next(gen)) print (next(gen)) # print(next(gen)) # StopIteration

(5) 斐波那契数列

"""使用生成器分段获取所有内容,而不是一股脑的把所有数据全部打印"""
"""1 1 2 3 5 8 13 21 34 .... """

 def  mygen(maxval):
a,b
= 0,1 i = 0 while i < maxval: # print(b) yield b
a,b
= b,a+ b
i
+= 1 # mygen(10) gen = mygen(10 ) # 第一次获取 for i in range(3 ): print (next(gen)) # 第二次获取 for i in range(5 ): print (next(gen))

 

 

递归函数

递归函数 : 自己调用自己的函数 , 叫做递归函数
递 : 去
归 : 回
一去一回叫做递归

 

 def  digui(n):  print (n, "  <==1==>  "  )  if  n > 0:
digui(n
-1 ) print (n, " <==2==> " )

digui(
5 ) """ # 去的过程
n = 5 print(5,"<==1==>") if 5 > 0: digui(5-1) => digui(4) 代码阻塞在第12行
n = 4 print(4,"<==1==>") if 4 > 0: digui(4-1) => digui(3) 代码阻塞在第12行
n = 3 print(3,"<==1==>") if 3 > 0: digui(3-1) => digui(2) 代码阻塞在第12行
n = 2 print(2,"<==1==>") if 2 > 0: digui(2-1) => digui(1) 代码阻塞在第12行
n = 1 print(1,"<==1==>") if 1 > 0: digui(1-1) => digui(0) 代码阻塞在第12行
n = 0 print(0,"<==1==>") if 0 > 0: 不成立 print(0,"<==2==>") 到此最后一层函数空间彻底执行完毕

# 回的过程
回到上一层函数空间 n = 1 代码在第12行的位置,继续往下执行 print(1,"<==2==>")
回到上一层函数空间 n = 2 代码在第12行的位置,继续往下执行 print(2,"<==2==>")
回到上一层函数空间 n = 3 代码在第12行的位置,继续往下执行 print(3,"<==2==>")
回到上一层函数空间 n = 4 代码在第12行的位置,继续往下执行 print(4,"<==2==>")
回到上一层函数空间 n = 5 代码在第12行的位置,继续往下执行 print(5,"<==2==>")

到此递归函数执行结束..
打印 543210012345
""" """ 每次调用函数时,都要单独在内存当中开辟空间,叫做栈帧空间,以运行函数中的代码

递归总结:
(1)递归实际上是不停的开辟栈帧空间和释放栈帧空间的过程,开辟就是去的过程,释放就是回的过程
(2)递归什么时候触发归的过程:
1.当最后一层栈帧空间执行结束的时候,触发归的过程.
2.当遇到return返回值的时候终止当前函数,触发归的过程.
(3)递归不能无限的去开辟空间,可能造成内存溢出,蓝屏死机的情况,所以一定要给予跳出的条件(如果递归的层数太大,不推荐使用)
(4)开辟的一个个栈帧空间,数据是彼此独立不共享的.
"""

 

流程解析

 

 

 

 

 

 

 

 

 

 

 

 

递归原理图:


 

递归不能不限开辟空间

官方说法最大默认是1000层

 def  deepfunc():
deepfunc()
deepfunc()

 

 

 

 

 

1.使用递归实现任意数n的阶乘 

普通实现

 #  5! =5 *4*3*2*1 
n = 5 total = 1
 for  i  in  range(n,0,-1 ):
total
*= i print (total) # 120

 

递归实现

 def  jiecheng(n):  if  n <= 1 :  return  1
     return  jiecheng(n-1) * n  print (jiecheng(2 ))  #  jiecheng(1) => 1  #  jiecheng(2) => jiecheng(1) * 2 => 1 * 2  #  jiecheng(3) => jiecheng(2) * 3 => 1 * 2 * 3  #  jiecheng(4) => jiecheng(3) * 4 => 1 * 2 * 3 * 4  #  jiecheng(5) => jiecheng(4) * 5 => 1 * 2 * 3 * 4 * 5 
 print (jiecheng(5 ))  """  代码解析:
去的过程:
n = 5 return jiecheng(n-1) * n => jiecheng(4) * 5
n = 4 return jiecheng(n-1) * n => jiecheng(3) * 4
n = 3 return jiecheng(n-1) * n => jiecheng(2) * 3
n = 2 return jiecheng(n-1) * n => jiecheng(1) * 2
n = 1 return 1

回的过程:
n = 2 return jiecheng(1) * 2 => 1 * 2
n = 3 return jiecheng(2) * 3 => 1 * 2 * 3
n = 4 return jiecheng(3) * 4 => 1 * 2 * 3 * 4
n = 5 return jiecheng(4) * 5 => 1 * 2 * 3 * 4 * 5

到此程序结束:
返回 1 * 2 * 3 * 4 * 5
"""

 

2. 使用尾递归来实现任意数的阶乘

return 在哪调用,在哪返回
自己调用自己,且返回时非运算表达式,只是函数本身

特点:
  尾递归只开辟一个空间,不会无限的开辟,在一个空间里面去计算最后的结果进行返回,比较节省空间,有的解释器支持尾递归的调用特点
  但是cpython解释器目前不支持
写法:
  所有运算的值都在函数的参数中计算完毕,最后返回运算的参数;

 def  jiecheng(n,endval):  if  n <= 1 :  return  endval  return  jiecheng(n-1 , n * endval)
res
= jiecheng(5,1) # 5*4*3*2*1 print (res) """ 代码解析:
去的过程
n = 5 ,endval = 1 return jiecheng(n-1 , n * endval) => jiecheng(4,5*1) => 5*1*4*3*2
n = 4 ,endval = 5*1 return jiecheng(n-1 , n * endval) => jiecheng(3,5*1*4) => 5*1*4*3*2
n = 3 ,endval = 5*1*4 return jiecheng(n-1 , n * endval) => jiecheng(2,5*1*4*3) => 5*1*4*3*2
n = 2 ,endval = 5*1*4*3 return jiecheng(n-1 , n * endval) => jiecheng(1,5*1*4*3*2) => 5*1*4*3*2
n = 1 ,endval = 5*1*4*3*2 if n <= 1 成立 return endval
endval = 5*1*4*3*2
最下层空间的返回值 是 5*4*3*2*1 最上层接收到的返回值也是 5*4*3*2*1
最下层和最上层返回的结果是一致的,所以对于尾递归来说,只需要考虑去的过程,无需考虑回的过程即可完成;

 

图片解析

 

 

 

优化代码1

 def  jiecheng(n,endval=1 ):  if  n <= 1 :  return  endval  return  jiecheng(n-1 , n * endval)
res
= jiecheng(5,100) # 5*4*3*2*1 print (res, " <00000> " )

 

优化代码2 [把尾递归需要的参数值隐藏起来,避免篡改.]

 def  outer(n):  def  jiecheng(n,endval=1 ):  if  n <= 1 :  return  endval  return  jiecheng(n-1 , n * endval)  return  jiecheng(n,1) #  120 
 print (outer(5))

 

优化代码3(扩展)
闭包实现

 def  outer(n):
endval
= 1 def jiecheng(n):
nonlocal endval
if n <= 1 : return endval
endval
*= n return jiecheng(n-1 ) return jiecheng
func
= outer(5 ) print (func(5), " <===111==> " )

 

3.使用递归来完成斐波那契数列

1 1 2 3 5 8 13 21 34 ...

 def  feib(n):  if  n == 1  or  n == 2 :  return  1
        
     #  上一个结果 + 上上个结果 
     return  feib(n-1) + feib(n-2 )  print (feib(5 ))  """  # 代码解析:
n = 5 feib(5) => 3 + 2 => return 5
feib(4) + feib(3)
feib(3)+feib(2) feib(2)+feib(1) => 1 + 1 => 2
feib(2)+feib(1)+feib(2) => 1 + 1 + 1 => 3
"""

 

标签: python

添加新评论