Python (进阶 第二部)

目录

 

函数

 

闭包

匿名函数

迭代器

高阶函数

推导式

递归函数

 

 

 

 

 

 

 

 

 

函数

函数的定义:  功能( 包括一部分代码,实现某种功能,达成某个目的)

函数的特点:  函数可以反复调用 ,提高代码的复用性,提高开发效率,便于维护和管理.

函数的基本格式:

# 定义一个函数

def 函数名():
    code1
    code2


# 函数的调用

函数名()

函数的命名规范:

字母数字下划线, 首字母不能为数字.

严格区分大小写, 且不能使用关键字.

函数命名有意义,  且不能使用中文.

 

函数的参数:( 参数:配合函数运算的值)

 

 

 

形参实参一一对应

形参: 形式参数,(在函数的定义处)

  普通形参(位置)  默认形参  普通收集形参  命名关键字形参   关键字收集形参

实参: 实际参数,(在函数的调用处)

  普通实参 关键字实参

 

普通形参

var = 你好
def func(var):
    print(var)

func(var)

默认形参

var = '你好'

def func(var='大家好'):
    print(var)

func()

普通收集形参

list = ['徐峰','学峰','妮妮']

def func(*args):
    print(list[:])

func(list)

命名关键字形参

命名关键字参数一般情况下跟在普通收集形参的后面, 在函数的调用时,必须使用命名关键子参数;来进行赋值

#方式一:
def func(a,b,*,d,c):
    print(a,b) # 1 2
    print(d) # 3
    print(c) # 10

func(1,2,d = 3,c=10)



方式二:
def func(*args, c, **kwargs):
    print(args) # (1, 2, 3, 4, 5, 6)
    print(c) # 100
    print(kwargs) # {'a': 1, 'b': 2, 'd': 6}

func(1, 2, 3, 4, 5, 6, a=1, b=2, d=6, c=100)

关键字收集形参

def func(a,b,*,c,d):
    print(a,b)
    print(c,d)
dic = {"c":3,"d":4}
# **把字典里面的所有元素拿出来,拼装成键=值的参数形式,赋值给func进行调用
func(1,2,**dic,) # func( c=3, d=4 )

locals globals nonlocal (了解)

locals

locals 如果在全局,调用locals之后,获取的是打印之前的所有变量 返回字典,全局空间作用域.

def func():
    ff = 123
a =1
b =2
res = locals()
c =3
print(res)   #是一个大字典 截止到打印之前

c =4  # 打印之后的c 就获取不到

 

 locals 如果在局部,调用locals之后, 获取的是调用之前的所有变量, 返回字典, 局部空间作用域.

 

a1 = 10 

def func ():
    a = 1
    b = 2
    res =locals()
    c = 3
    print(res)    #截止到locals() 的调用之前( 就是locals之前的局部变量
    d =4   # 获取不到
b2 =22  # 获取不到
func()
a3 =33  #获取不到

 globals

globals如果在全局, 调用globals之后,获取的是打印之前的所有变量,返回字典,全局空间作用域

def func():
    ff = 123
a =1
b =2
res= globals()   # 获取打印之前的所有变量
c =3
print(res)
d =4    # c =4 将获取不到

globals 如果在局部, 调用globals之后,获取的是调用之前的所有变量,返回字典,全局空间作用域

a1 = 10
def func():
    a =1
    b =2
    res=globals()
    c =3
    print(res)
    d = 4
a2 =20
func()     # 获取的是调用之前的所有变量

a3=30  # 获取不到

 nonlocal

nonlocal遵循LEGB就近找变量的原则

  • 找当前空间上一层的局部变量进行修改
  • 找不到继续向再外一层找
  • 最后一层也没有时,就会报错

 

1. 找当前空间上一层的局部变量进行修改

def outer():
    a =100
    def inner():
        nonlocal a
        a =200
        print(a)
    inner()
    print(a)
outer()

# outer层中的变量被inner中的nonlocal修改后,inner和outer在调用时都将得到的是修改之后的值

2. 如果找不到继续向上一层找

def outer():
    a=10
    def inner():
        b=20
        def func():
            nonlocal a 
            a =1
            print(a)
        func()
        print(a)
    inner()
    print(a)
outer()

# func() 中的nonlocal 将 a 修改为1 在inner层中没有,就就找的outer中找到并修改 ,此时可以到的都是修改后的变量

3. 注意点: nonlocal只能修改局部变量

 

a =1
def outer():
    b =2
    def inner():
        c = 3
        def func():
            nonlocal d
            d =4
            print(d)
        func()
        print(a)
    inner()
    print(a)
outer()

# SyntaxError: no binding for nonlocal 'd' found

4. 不使用nonlocal 修改局部变量

def func():
    lst = [1,2,3,4]
    def inner():
        lst[-1] = 10
    inner()
    print(lst)
func()

函数的嵌套

1. 函数之间可以互相嵌套

  外层的函数叫做外函数,内层的叫做内函数

  • (!)内部函数不可以在函数外部调用
  • (2)调用外函数后, 内函数不可以在函数外部调用
  • (3)内函数只能在函数内使用
  • (4)内函数在函数内部调用时,必须先定义再使用

2. 外层是outer  ,内层是inner  ,最里层是smaller  ,调用smaller 里面的所有代码

def outer():
    def inner():
        def smaller():
            print('我是smaller函数')
        smaller()
    inner()
outer()

3. LEGB 原则(就近找变量原则)

 

B —— Builtin(Python);Python内置模块的命名空间 (内建作用域)
G —— Global(module); 函数外部所在的命名空间 (全局作用域)
E —— Enclosing function locals;外部嵌套函数的作用域(嵌套作用域)
L —— Local(function);当前函数内的作用域 (局部作用域)
依据就近原则,从下往上 从里向外 依次寻找

 

函数的返回值 return

自定义函数的返回值 return , 可以把值返回到函数的调用处

(1) return + 六大标准数据类型 , 类 , 对象,函数

  如果不定义return  ,默认返回的None

(2) 执行完return 之后, 立刻终止函数, 后面的代码将不会被执行

利用return 模拟简单

 
def func(sign,num1,num2):
if sign =='+':
res = num1 + num2
elif sign =='-':
res = num1 - num2
elif sign =='*':
res = num1 * num2
elif sign=='/':
if num2 ==0:
return '除数不能为0'
res = num1 / num2
else:
return '这个计算不出来'
return res
res = func("-",10,20)
print(res)

全局变量和局部变量

局部变量 : 在函数内部定义的变量(局部命名空间)
全局变量 : 在函数外部定义的或者使用global在函数内部定义(全局命名空间)

作用域: 作用的范围
局部变量作用域: 在函数的内部
全局变量作用域: 横跨整个文件

生命周期:
内置变量 > 全局变量 > 局部变量总结:

可以使用global 关键字在函数内部定义一个全局变量
也可以使用global关键字在函数内部修改一个全局变量

函数名的使用

    1. 函数名是个特殊的变量,可以当做变量赋值
      a = “你好”
      print(a)
      a = func
      a()

      函数可以像变量一样销毁

    2. 函数名可以作为容器类型数据的元素

    3. 函数名可以作为函数的参数

    4. 函数名可作为函数的返回值

 

 

闭包

如果内函数使用了外函数的局部变量,并且外函数把内函数返回的过程,叫做闭包.

被外函数返回出的函数就是闭包函数

基础语法

def songyunjie_family():
 father = "王健林"
 def f_hobby():
  print("我们先顶一个小目标,比如赚它一个亿,这是我爸爸{}".format(father))
 return f_hobby
func = songyunjie_family()
func()
obj = func.__closure__[0]
print(obj.cell_contents,"<111>")

复杂版

def mashengping_family():
 father = "马云"
 jiejie_name = "马蓉"
 meimei_name = "马诺"
 money = 1000
 def jiejie():
  nonlocal money
  money -= 700
  print("买包包,买名表,买首饰,把钱都败光了,家里的钱还剩下{}元".format(money)) 
 def meimei():
  nonlocal money
  money -= 200
  print("要找只找有钱人,宁愿在宝马里面哭,也不愿意在自行车上撒欢,家里的败光了还剩下{}元".format(money))
 def big_master():
  return (jiejie,meimei)
 return big_master 
func = mashengping_family()
print(func)
# 返回的是元组
tup = func() # big_master()
print(tup)   # tup =  (jiejie,meimei)
# 获取姐姐
jiejie = tup[0] # jiejie 函数
# 获取妹妹
meimei = tup[1] # meimei 函数

获取闭包函数使用的变量 closure

  res= func.closure

  print(res,'<222>')

  cell_contents 用来获取单元格对象当中的闭包函数

  jiejie = res[0].coll_contenrs

  meimei = res[1].cell_contents

通过获取单元格对象 获取单元格对象中的内容 实际的调用

  jiejie()

  meimei()

  print(jiejie.closure[0].coll_contents)

  print(meimei.closure[1].coll_contents)

闭包的特征

内函数使用了外函数的局部变量 ,那么该函数与闭包函数就发生了绑定,延长了这个函数的生命周期

def outer(val):
 def inner(num):
  return val + num
 return inner 
func = outer(10) # func = inner
res = func(15)   # res = func(15) = inner(15)
print(res)

闭包的意义

闭包可以优先使用外函数中的变量,并对闭包函数中的值起到了封装保护的作用,外部无法访问.

模拟鼠标点击次数

num = 0
def click_num():
 global num
 num += 1
 print(num)
click_num()
click_num()
click_num()
num = 100
click_num()
click_num()

 

匿名函数

用一句话来表达只有返回值的函数

  1. 语法: lambda 参数: 返回zhi
  2. 追求代码简洁高效
  3. 无参的lambda表达式
    1. def func():
       return "123"
      func = lambda : "123"
      res = func()
      print(res)

       

  4. 有参的lambda表达式
    1. def func(n):
       return type(n)
      
      func = lambda n : type(n)
      print(  func([1,2,3])  )

       

  5. 带有判断条件的lambda表达式
    1. def func(n):
       if n % 2 == 0:
        return "偶数"
       else:
        return "奇数"
      
      func = lambda n : "偶数" if n % 2 == 0 else "奇数"
      res = func(17)
      print(res)

       

  6. 三元运算
    1. n = 16
      res = "偶数" if n % 2 == 0 else "奇数"
      print(res)
      
      def func(x,y):
       if x>y:
        return x
       else:
        return y  
      func = lambda x,y : x if x>y else y
      res = func(200,100)
      print(res)

       

迭代器

能被next 调用,并不断返回下一个值的对象

迭代器的概念:迭代器指的就是迭代取值的工具,迭代是一个重复的过程,每一次迭代都是基于上一次的结果而继续的

迭代器的特征:不依赖索引,而是通过next 的指针迭代所有的数据,一次只能取一个值,大大节省了空间

dir 获取当前类型对象中的所有成员

“”“iter 方法用来判断是否是可迭代性数据”""
lst = dir(setvar)
print(dir(“123”))
res = “iter” in dir(setvar)
print(res)

 

迭代器

  • for 循环能够遍历一切可迭代性数据的原因在于,底层调用了迭代器,
  • 通过next方法中的指针实现数据的获取
  • 可迭代对象 -> 迭代器 不能够被next直接调用 -> 可以被next直接调用的过程
  • 如果是一个可迭代对象不一定是迭代器
  • 但如果是一个迭代器就一定是一个可迭代对象

如何定义一个迭代器

setvar = {“a”,“b”,“c”,“d”}
it = iter(setvar)
print(it)

 

如何判断一个迭代器

print(dir(it))
res = “iter” in dir(it) and “next” in dir(it)
print(res)

 

如何来调用一个迭代器

“”“next在调用迭代器中的数据时,是单向不可逆,一条路走到黑的过程”""
res = next(it)
print(res)
res = next(it)
print(res)
res = next(it)
print(res)
res = next(it)
print(res)
“”"
#StopIteration 报错 停止迭代
res = next(it)
print(res)
“”"

 

重置迭代器

it = iter(setvar)
res = next(it)
print(res)

 

使用其他方法判断迭代器或者可迭代对象

“”“Iterator 迭代器 Iterable 可迭代对象”""
from … 从哪里 import 引入 …
from collections import Iterator,Iterable
from collections.abc import Iterator,Iterable python3.7
res = isinstance(it,Iterator)
print(res)
res = isinstance(it,Iterable)
print(res)

 

使用其他方法调用迭代器中的数据

for i in it:
print(i)

 

for+next

lst = [1,2,3,4,5,6,7,7,8,9,10]
it = iter(lst)
for i in range(10):
res = next(it)
print(res)
print(next(it))
print(next(it))

 

高阶函数

1. map 元素加工

  map(func,lierable)

  功能:处理数据

  把iterable 中的数据一个一个取出来,放到func函数中进行加工处理后将结果放到迭代器当中,最后返回迭代器

  参数: fuunc 自定义函数,或者内置函数

  iterable: 可迭代性数据( 容器类,range,迭代器)

  返回值: 迭代器

# 常规写法
lst_new = []
for i in lst:
 lst_new.append(int(i))
print(lst_new)
# map改造
it = map(int,lst)

2. filter  数据过滤

  filter(func, iterable)   

  功能: return Turn 当前这个数据保留

    return False 当前这个数据舍弃

  func: 自定义函数

  iterable: 可迭代性数据( 容器型, range, 迭代器)

  返回值: 迭代器

# 常规写法
lst_new = []
for i in lst:
 if i % 2 == 0:
  lst_new.append(i)  
print(lst_new)
# filter改写
def func(i):
 if i % 2 == 0:
  return True
 else:
  return False
it = filter(func,lst)
# (1) next
res = next(it)
print(res)

3. reduce

  reduce(func, iterable)   

  功能: 计算数据

  先把iterable中的前俩个值取出,放到func中计算,把结算的结果和iterable中的第三个元素在放到func中计算,依次类推,直到所有结果都运算完毕,返回结果

 

  func: 自定义函数

  iterable: 可迭代数据(容器,range, 迭代器)

  返回值: 计算之后的结果

# 常规写法
lst = [5,4,8,8] # => 整型5488
# 方法一
strvar = ""
for i in lst:
 strvar += str(i)
print(strvar , type(strvar))
res = int(strvar)
print(res , type(res))

4. sorted

  sorted( iterable , key=func,reverse = False)

  功能 : 排序

  iterable : 可迭代数据( 容器,range,迭代器)

  key:指定自定义函数,内置函数

  reverse: 代表升序或者降序默认是升序( 从小到大排序) reverse=False

  返回值: 排序后的结果

# 1.默认是从小到大排序
lst = [1,2,3,4,5,-90,-4,-1,100]
res = sorted(lst)
print(res)
# 2.reverse 从大到小排序
res = sorted(lst,reverse=True)
print(res)
# 3.指定函数进行排序
# 按照绝对值排序 abs
lst = [-10,-1,3,5]
res = sorted(lst,key=abs)
"""
-1  => abs(-1)  => 1
3  => abs(3)  => 3
5  => abs(5)  => 5
-10 => abs(-10) => 10
[-1, 3, 5, -10]
"""
print(res)
# 4.使用自定义函数进行排序
lst = [19,21,38,43,55]
def func(n):
 return n % 10
lst = sorted(lst,key=func)
print(lst)
"""
21 => n % 10 => 1
43 => n % 10 => 3
55 => n % 10 => 5
38 => n % 10 => 8
19 => n % 10 => 9
21 43 55 38 19
"""
# ### sorted 和 sort 之间的区别
#字符串
container = "eadc"
#列表
container = [19,21,38,43,55]
#元组
container = (19,21,38,43,55)
#集合
container = {19,21,38,43,55}
#字典 (排序的是字典的键)
container = {"c":3,"a":1,"b":2}
container = {"王闻":3,"高云峰":2}
print("<===>")
res = sorted(container)
print(res)
#(1) sorted可以排序一切容器类型数据, sort只能排列表
#(2) sorted返回的是新列表,sort是基于原有的列表进行修改
#(3) 推荐使用sorted

 

  

 

  

推导式

语法: 

  val for val in iterable

 

三种推导式方式

[val for val initerable]

{val for val in iterable}

{ k:v for k,v in  iterable }

 

1. 基础语法:

推导式的语法:
 val for val in Iterable
 三种方式:
  [val for val in Iterable]
  {val for val in Iterable}
  {k:v for k,v in Iterable}
  # 改写成推导式
lst = [i*3 for i in lst]
print(lst)

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

# (2) 带有判断条件的单循环推导式 (只能是单项分支,接在for后面)
lst = [1,2,3,4,5,6,7,8]
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. 双循环推导式

# (3) 双循环推导式
lst1 = ["李博伦","高云峰","孙致和","葛龙"]
lst2 = ["李亚","刘彩霞","刘子豪","刘昕"]
# "谁"❤"谁"
lst_new = []
for i in lst1:
 for j in lst2:
  strvar = i + "" + j
  lst_new.append(strvar)
print(lst_new)
# 改写成推导式
lst = [i + "" + j for i in lst1 for j in lst2]
print(lst)

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

# (4) 带有判断条件的多循环推导式
lst_new = []
for i in lst1:
 for j in lst2:
  if lst1.index(i) == lst2.index(j):
   strvar = i + "" + j
   lst_new.append(strvar)   
print(lst_new)
# 改写成推导式
lst = [ i + "" + j for i in lst1 for j in lst2 if lst1.index(i) == lst2.index(j) ]
print(lst)

集合推导式

常规写法

# 常规写法
setvar = set()
for i in listvar:
 if 18 <= i["age"] <= 21 and  5000 <= i["money"] <= 5500:
  res = "尊贵VIP卡老" + i["name"][0]
 else:
  res = "抠脚大汉卡老" + i["name"][0]
 setvar.add(res)
print(setvar)
# 改写成集合推导式
# {三元运算符 + 推导式}
setvar = { "尊贵VIP卡老" + i["name"][0] if 18 <= i["age"] <= 21 and  5000 <= i["money"] <= 5500 else "抠脚大汉卡老" + i["name"][0] for i in listvar }
print(setvar)

字典推导式

enumerate

enumerate(iterable,[start=0])
功能:枚举 ; 将索引号和iterable中的值,一个一个拿出来配对组成元组放入迭代器中
参数:
    iterable: 可迭代性数据 (常用:迭代器,容器类型数据,可迭代对象range) 
    start:  可以选择开始的索引号(默认从0开始索引)
返回值:迭代器
"""
from collections import Iterator
lst = ["东邪","西毒","南帝","北丐"]
# 基本使用
it = enumerate(lst)
print(isinstance(it,Iterator))
# for + next
for i in range(4):
 print(next(it))
# list
"""start可以指定开始值,默认是0"""
it = enumerate(lst,start=1)
print(list(it))
# enumerate 形成字典推导式 变成字典
dic = { k:v for k,v in enumerate(lst,start=1) }
print(dic)
# dict 强制变成字典
dic = dict(enumerate(lst,start=1))
print(dic)

zip

"""
zip(iterable, ... ...)
    功能: 将多个iterable中的值,一个一个拿出来配对组成元组放入迭代器中
    iterable: 可迭代性数据 (常用:迭代器,容器类型数据,可迭代对象range) 
返回: 迭代器
特征: 如果找不到对应配对的元素,当前元素会被舍弃
"""
# 基本使用
lst1 = ["晏国彰","刘子涛","郭凯","宋云杰"]
lst2 = ["刘有右柳翔","冯雍","孙志新"]
lst3 = ["周鹏飞","袁伟倬"]
# it = zip(lst1,lst2)
it = zip(lst1,lst2,lst3)
print(isinstance(it,Iterator))
print(list(it))
"""
[('晏国彰', '刘有右柳翔'), ('刘子涛', '冯雍'), ('郭凯', '孙志新')]
[('晏国彰', '刘有右柳翔', '周鹏飞'), ('刘子涛', '冯雍', '袁伟倬')]
"""
# zip 形成字典推导式 变成字典
lst1 = ["晏国彰","刘子涛","郭凯","宋云杰"]
lst2 = ["刘有右柳翔","冯雍","孙志新"]
dic = { k:v for k,v in zip(lst1,lst2) }
print(dic)
# dict 强制变成字典
dic = dict(zip(lst1,lst2))
print(dic)

 

生成器表达式

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

 

迭代器与生成器的区别:

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

生成器的俩种创建方式

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

  2. 生成器函数( 用def定义 ,里面含有yield)

 from collections import Iterator,Iterable
# 生成器表达式
gen = (i*2 for i in range(1,11))
print(isinstance(gen,Iterator))
# next 
res = next(gen)
print(res)
# for 
for i in gen:
 print(i)
# for + next
gen = (i*2 for i in range(1,11))
for i in range(3):
 res = next(gen)
 print(res)
# list
print("<=====>")
res = list(gen)
print(res)

生成器函数

yield 类似于 return

  共同点: 执行到该点时,都会把值返回出去.

  不同点: yield 每次返回时 ,会记住上次离开时执行的位置,下次在调用生成器,会从上次执行的位置继续向下执行,而return,会直接终止函数,不可以重头调用.

yield 6 和yield(6) 俩种写法都可以 yield 6 更像 return 6 的写法 ( 推荐使用)

生成器函数的基本用法

 定义一个生成器函数
def mygen():
 print(111)
 yield 1 
 print(222)
 yield 2
 print(333)
 yield 3
#初始化生成器函数,返回生成器对象,简称生成器
gen = mygen()
print(isinstance(gen,Iterator))
#使用next调用
res = next(gen)
print(res)
res = next(gen)
print(res)
res = next(gen)
print(res)
#res = next(gen) error
#print(res)
"""
代码解析:
初始化生成器函数 -> 生成器(通过next调用)
第一次调用生成器
res = next(gen) => print(111) yield 1 保存当前代码状态14行,并将1这个值返回 print(1) ,等待下一次调用
第二次调用生成器
res = next(gen) => 从上一次保存的状态14行继续向下执行
print(222) yield 2 保存当前代码状态17行,并将2这个值返回 print(2) ,等待下一次调用
第三次调用生成器
res = next(gen) => 从上一次保存的状态17行继续向下执行
print(333) yield 3 保存当前代码状态20行,并将3这个值返回 print(3) ,等待下一次调用
第四次调用生成器
因为没有更多yield返回数据了,所以直接报错.
"""

优化代码

def mygen():
for i in range(1,101):
yield “该球衣号码是{}”.format(i)
#初始化生成器函数 -> 生成器
gen = mygen()
#for + next 调用数据
for i in range(50):
res = next(gen)
print(res)
print("<====>")
for i in range(30):
res = next(gen)
print(res)

send 的用法

# 3.send 用法
"""
### send
# next和send区别:
 next 只能取值
 send 不但能取值,还能发送值
# send注意点:
 第一个 send 不能给 yield 传值 默认只能写None
 最后一个yield 接受不到send的发送值
 send 是给上一个yield发送值 
"""
def mygen():
 print("process start")
 res = yield 100
 print(res,"内部打印1") 
 res = yield 200
 print(res,"内部打印2")
 res = yield 300
 print(res,"内部打印3")
 print("process end")
# 初始化生成器函数 -> 生成器
gen = mygen()
# 在使用send时,第一次调用必须传递的参数是None(硬性语法),因为第一次还没有遇到上一个yield
'''第一次调用'''
res = gen.send(None) #<=> next(gen)
print(res)
'''第二次调用'''
res = gen.send(101) #<=> next(gen)
print(res)
'''第三次调用'''
res = gen.send(201) #<=> next(gen)
print(res)
'''第四次调用, 因为没有更多的yield返回数据了,所以StopIteration'''
"""
res = gen.send(301) #<=> next(gen)
print(res)
"""
"""
# 代码解析:
初始化生成器函数,返回生成器对象
第一次调用时,
print("process start")
res = yield 100  记录当前代码状态81行,返回100,等待下一次调用
res = 100 print(100)
第二次调用时,
把101 发送给上一个yield保存的状态81行 res = 101 从81行继续往下走
print(101,"内部打印1")
res = yield 200  记录当前代码状态84行,返回200,等待下一次调用
res = 200 print(200)
第三次调用时,
把201 发送给上一个yield保存的状态84行 res = 201 从84行继续往下走
print(201,"内部打印2")
res = yield 300  记录当前代码状态87行,返回300,等待下一次调用
res  = 300 print(300)
"""

yield from : 将一个迭代器对象变成一个迭代器返回

# 4.yield from : 将一个可迭代对象变成一个迭代器返回 
def mygen():
 yield from ["马生平","刘彩霞","余锐","晏国彰"] 
gen = mygen()
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))
# 5.用生成器描述斐波那契数列
"""1 1 2 3 5 8 13 21 34 ... """
"""
yield 1
a,b = b,a+b = 1,1
yield 1
a,b = b,a+b = 1,2
yield 2
a,b = b,a+b = 2,3
yield 3
a,b = b,a+b = 3,5
yield 5
....
"""
def mygen(maxlen):
 a,b = 0,1
 i = 0
 while i < maxlen:
  yield b
  a,b = b,a+b
  i+=1 
# 初始化生成器函数 -> 生成器
gen = mygen(10)
for i in range(3):
 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===>") 5>0 条件成立-> digui(5-1) => digui(4) 代码阻塞在第13行
n = 4 print(4,"<====1===>") 4>0 条件成立-> digui(4-1) => digui(3) 代码阻塞在第13行
n = 3 print(3,"<====1===>") 3>0 条件成立-> digui(3-1) => digui(2) 代码阻塞在第13行
n = 2 print(2,"<====1===>") 2>0 条件成立-> digui(2-1) => digui(1) 代码阻塞在第13行
n = 1 print(1,"<====1===>") 1>0 条件成立-> digui(1-1) => digui(0) 代码阻塞在第13行
n = 0 print(0,"<====1===>") 0>0 条件不成立 print(0,"<====2===>") 
当前这层空间代码已经执行结束
此刻触发回的过程
n = 1 从上一次13行的代码阻塞位置,继续向下执行 print(1,"<====2===>")
n = 2 从上一次13行的代码阻塞位置,继续向下执行 print(2,"<====2===>")
n = 3 从上一次13行的代码阻塞位置,继续向下执行 print(3,"<====2===>")
n = 4 从上一次13行的代码阻塞位置,继续向下执行 print(4,"<====2===>")
n = 5 从上一次13行的代码阻塞位置,继续向下执行 print(5,"<====2===>")
到此,递归函数彻底执行结束.
5 4 3 2 1 0 0 
"""

每一次调用函数时 ,在内存中都会单独开辟一片空间,配合函数运行,这个空间叫做栈帧空间

1).递归是一去一回的过程,
 调用函数时,会开辟栈帧空间,函数执行结束之后,会释放栈帧空间
 递归实际上就是不停的开辟和释放栈帧空间的过程
 每次开辟栈帧空间,都是独立的一份,其中的资源不共享
(2).触发回的过程
 1.当最后一层栈帧空间全部执行结束的时候,会触底反弹,回到上一层空间的调用处
 2.遇到return,会触底反弹,回到上一层空间的调用处, 
(3).写递归时,必须给与递归跳出的条件,否则会发生内存溢出,蓝屏死机的情况.
 如果递归层数过多,不推荐使用递归

"""
# 官方说法:默认1000层,实际996层左右
"""
def func():
 func()
func()
"""

 

递归阶乘

# (1) 用递归计算n的阶乘
# 常规方法
# 5! = 5*4*3*2*1
def func(n):
 total = 1
 for i in range(n,0,-1):
  total *= i
 return total
res = func(5)
print(res)
# 递归写法
def jiecheng(n):
 if n <= 1:
  return 1
 return n*jiecheng(n-1)
res = jiecheng(5)
print(res)
"""
return 后面的表达式,一定是先计算完在返回
# 代码解析:
# 去的过程:
n = 5   return 5*jiecheng(5-1) => 5 * jiecheng(4)
n = 4   return 4*jiecheng(4-1) => 4 * jiecheng(3)
n = 3   return 3*jiecheng(3-1) => 3 * jiecheng(2)
n = 2   return 2*jiecheng(2-1) => 2 * jiecheng(1)
n = 1   return 1
# 回的过程:
n = 2   return 2*jiecheng(2-1) => 2 * jiecheng(1) => 2 * 1
n = 3   return 3*jiecheng(3-1) => 3 * jiecheng(2) => 3 * 2 * 1
n = 4   return 4*jiecheng(4-1) => 4 * jiecheng(3) => 4 * 3 * 2 * 1
n = 5   return 5*jiecheng(5-1) => 5 * jiecheng(4) => 5 * 4 * 3 * 2 * 1
return 5 * 4 * 3 * 2 * 1 => return 120
额外解析:
jiecheng(1) => 1
jiecheng(2) => 2*jiecheng(1) => 2*1
jiecheng(3) => 3*jiecheng(2) => 3*2*1
jiecheng(4) => 4*jiecheng(3) => 4*3*2*1
jiecheng(5) => 5*jiecheng(4) => 5* 4*3*2*1
"""
print(jiecheng(1))

 

尾递归

"""
自己调用自己,并且非表达式
计算的结果要在参数当中完成.
尾递归无论调用多少次函数,都只占用一份空间,但是目前cpython不支持.
"""
def jiecheng(n,endval):
 if n <= 1:
  return endval
 return jiecheng(n-1,endval*n)
res = jiecheng(5,1)
print(res)
"""
# 代码解析:
去的过程
n=5 , endval=1         return jiecheng(5-1,endval*5) => jiecheng(4,1*5)
n=4 , endval=1*5       return jiecheng(4-1,endval*4) => jiecheng(3,1*5*4)
n=3 , endval=1*5*4     return jiecheng(3-1,endval*3) => jiecheng(2,1*5*4*3)
n=2 , endval=1*5*4*3   return jiecheng(2-1,endval*2) => jiecheng(1,1*5*4*3*2)
n=1 , endval=1*5*4*3*2 return 120
回的过程:
n=2 return 120
n=3 return 120
n=4 return 120
n=5 return 120
因为最后一层空间的返回值就是第一层空间的返回值,所有在使用尾递归的时候
不需要考虑回的逻辑过程,就能解决问题.推荐使用.
"""
# 优化1
def jiecheng(n,endval=1):
 if n <= 1:
  return endval
 return jiecheng(n-1,endval*n)
res = jiecheng(5)
print(res,"<111>")
# 优化2
"""为了避免用户乱传参数,把endval这个参数隐藏起来"""
def outer(n):
 def jiecheng(n,endval=1):
  if n <= 1:
   return endval
  return jiecheng(n-1,endval*n) 
 return jiecheng(n) # jiecheng(n-1,endval*n)
res = outer(5)
print(res)

 

递归斐波那契数列

# 递归计算斐波那契数列
""" 1,1,2,3,5,8,13 ....  
n = 3 => 2
n = 5 => 5
n = 6 => 8
"""
# 上一个 n-1 上上个 n-2
def feb(n):
 # 递归跳出的条件
 if n <= 2: # n == 1 or n == 2 => 1
  return 1
 return feb(n-1) + feb(n-2)
res = feb(5)
print(res)
"""
代码解析:
n = 5 return feb(5-1) + feb(5-2) => feb(4) + feb(3)
        feb(4)->3      +      feb(3)->2    => 5
   feb(3) +  feb(2)      feb(2) + feb(1)
feb(2)+feb(1) | 1           1   +   1
       2  +  1
"""

 

posted @ 2020-10-31 21:36  liuun  阅读(130)  评论(0编辑  收藏  举报