python入门到放弃-函数专题

一、函数的定义

函数是对代码块和功能的封装和定义
#函数的语法:def是define的意思,定义
最基本的语法:
    def 函数名():
        函数体
   函数名() #调用函数
带有参数的语法
def 函数名(形参列表):
    函数体(代码块,return)
    
函数名(实参列表) :调用
#例子:函数执行过程
# def wan():  #定义函数
#     print("今天一起去玩")
#     print("去哪里玩呢")
#     print("我不知道")
# wan()  #调用函数
‘‘‘讲解执行的过程
    1.定义函数wan()
    2.调用函数wan()
    3.准备开始执行函数
    4.打印,今天一起去玩
    5.打印,去哪里完
    6.打印,我不知道
    7.函数执行完毕,本次调用完毕,wan()函数调用完毕
‘‘‘

二.return返回值的相关操作

return:在函数执行的时候,如果遇到return,则直接返回,和while循环中break一样
    1、如果函数什么都不写,不写return,没有返回值,得到的是Nano
    2、在函数中间或者末尾写return,返回的是None
    3、在函数中写 return 值,返回的是一个值
    4、在函数中有返回多个返回值,return 值1、值2、值3... 返回接收到的是元组

#例子:    
#1、函数什么都不写,不写return,返回的是None
def wan():
    print("今天一起去玩")
    print("去哪里玩呢")
    print("我不知道")
ret = wan()
print(ret)  #None

#2、在函数中间或者末尾写return,返回的是None
def wan():
    print("今天一起去玩")
    return
    print("去哪里玩呢")
    print("我不知道")
ret = wan()
print(ret)  #在执行完第一个print的时候就返回None,就结束了

#3、在函数中写一个return 值,返回的是一个值
def wan():
    print("今天一起去玩")
    return "锅盖"
    print("去哪里玩呢")
    print("我不知道")
ret = wan()
print(ret)
#今天一起去玩
#锅盖
#会看到返回锅盖就结束了

#4、函数中有返回多个返回值,那么返回的是一个元组
def wan():
    print("今天一起去玩")
    return "锅盖","番薯","大块"
    print("去哪里玩呢")
    print("我不知道")
ret = wan()
print(ret)  ##(‘锅盖‘, ‘番薯‘, ‘大块‘)

相关操作

三.函数的参数

函数在调用的时候指定具体的一个变量的值,就是参数

#参数包括:形参,实参,传参

形参:函数声明的位置的变量
实参:函数调用的时候给的具体的值
传参:把实参交给形参的过程

#相关参数具体位置例子

#形参和实参的位置
# def wan(形参):  #在函数声明的位置的变量就是形参
#     print(好玩)
# 
# wan(实参) :#在函数调用的地方给的具体的值就是实参

#例子
# def wan(what):
#     print("手机")
#     print("去哪里"+what)
# wan("广西")
#在调用wan的时候给what一个值,然后执行函数体

#实参的相关操作

#包括
    1.位置参数:按照形参的参数位置,给形参传值
    2.关键字参数:按照形参的名字给形参传值
    3.混合参数:即用位置参数,也用关键字参数

#实参操作的例子:

# 1、位置参数,按照形参的位置,给形参传值
#例子
# def chi(moning,after,night):
#     print(moning,after,night)
# chi("玉米饺","米饭","面条") #玉米饺 米饭 面条


#2.关键字参数: 按照形参的名字给形参传值
# def chi(moning,after,night):
# #     print(moning,after,night)
# # chi(after="米饭",moning="饺子",night="面条") #饺子 米饭 面条

# 3.混合参数:即用位置参数,也用关键字参数
# def chi(moning,after,night):
#     print(moning,after,night)
# chi("饺子","米饭",night="面条") #饺子 米饭 面条

#注意:
    #顺序位置:要先写位置后再写关键字,要不然会报错
#例子
# chi("饺子",night="面条","米饭")  #会报红色
#SyntaxError: positional argument follows keyword argument
#语法错误:关键字参数后面跟了位置参数

#形参的相关操作

#包括
    1.位置参数
    2.默认值参数,要先写位置参数之后才能写默认值参数
    3.动态参数     包括:位置参数动态传参 *args,关键字参数动态传参**kwargs      无敌传参方法:def func(*args,**kwargs):

#形参操作的例子:

#1.位置参数,按照位置来进行赋值
# def chi(moning,after,night):
#     print(moning)
#     print(after)
#     print(night)
# chi("饺子","米饭","面条")


#2.默认值参数
#例子:比如一个班上要录入学生信息,有大部分都是男生,就可以设置一个默认值是男
# def stu_inf(name,age,sex=‘男‘):
#     print("录入学生信息")
#     print(name,age,sex)
#     print("录入完毕")
# stu_inf("蒋小鱼",18)
# stu_inf("张冲",22)
# stu_inf("沈搁",22,sex="女") #如果不想使用默认值也可以自己设定

#注意点:
    #必须先声明位置参数,才能声明默认值参数,否则会有问题
#例子
# def stu_inf(name,sex=‘男‘,age): #很明显这种写法是错误的


#3.位置参数的动态传参
#     * 在这里表示接收位置参数的动态传参,接收到的是元组
# def chi(*foot):  #参数名是food, * 表示动态传参
#     print(foot)
# chi("米饭","面条","饺子")  #(‘米饭‘, ‘面条‘, ‘饺子‘)
# chi("馒头")
# chi()

# def chi(name,*food,location="河北"):
#     print(location)
#     print(name+"要吃",food)
# chi("张三","饺子","面条")

#要注意参数的书写顺序
#顺序: 位置参数 -> 位置参数动态传参 -> 默认值参数
#错误写法
# def chi(name,location="河北",*food):
#     print(location)
#     print(name+"要吃",food)
# chi("张三","饺子","面条")   #这样写的话饺子就成了默认值
# 饺子
# 张三要吃 (‘面条‘,)
‘‘‘


#关键字的动态传参
‘‘‘
# def chi(**food):
#     print(food)
#
# chi(good_food="大米",no_good_food="面条",dirnk="水")
#前面得是变量,如果写数字或者字符串这种就会报错,如:"大海" = "虾"

# def chi(*food2,**food):
#这样是无敌传参
#     print(food)
#
# chi(good_food="大米",no_good_food="面条",dirnk="水")

#提示:
#   位置参数,*args(位置关键字动态传参) 默认值参数 **kwargs 关键字动态传参
#   以上参数可以任意搭配使用,但是要注意顺序问题,要不然会有问题

形参相关操作例子

#参数的位置顺序排列

位置参数->位置参数动态传参*args->默认值参数->关键字参数动态传参**kwargs

四.函数的注释

在函数里面用三个单引号或三个双引号引起来的就是函数的注释

#例子:写好注释让别人能看明白,能省去很多事情

# def chi(food,drink):
#     """
#     这里是函数的注释,先写一下当前这个函数是干什么的,不如我这个函数就是一个吃
#     :param food: 参数food是什么意思
#     :param drink:  参数drink是什么意思
#     :return:   返回的是什么东西
#     """
#     print(food,drink)
#     return "good"
# print(chi.__doc__)   #document 文档
# print(str.__doc__) #查看字符串的文档注释

#关于参数的聚合和打散

#形参:聚合
# def func(*food):
#     print(food)
# lst = ["大麻花","饺子","面条","土豆"]
# func(lst) #这样调用的话是一个列表在里面的

# # 实参: 打散
# func(lst[0],lst[1],lst[2],lst[3]) #将上面的大三, 把list,tuple,set,str进行迭代打散
# func(*lst)  #上面的打散写不方便,写*号更方便简单
# #但是上面的打散都是一个一个的,那么字典呢?

#字典的打散方式
#聚合成关键字参数
# def func(**kwargs):
#     print(kwargs)
# dic = {"name":"sir","age":"18"}
# func(**dic)  #打散成关键字参数

打散和聚合例子

五.函数名

函数名也是一个变量,但是一个特殊的变量,与括号配合可以执行函数的变量

#函数名的相关操作

1.函数名可以赋值给其他变量
2.函数名可以作为参数列表中的元素进行存储,作容器类的元素
3.函数名可以作为参数传递给函数
4.函数名可以作为函数的返回值

#函数名相关操作例子:

# 1、函数名的内存地址
# def func():
#     print("哈哈")
# print(func)  #因为还没有进行调用
#结果:<function func at 0x0000025A9344C1E0>


# 2.函数名可以赋值给其他变量
# def func():
#     print("哈哈")
#
# print(func)
# a = func #把函数当成一个变量赋值给另一个变量
# a()  #函数调用func()
#打印哈哈


# 3.函数名可以当作容器类的元素
# def func1():
#     print("哈哈")
#
# def func2():
#     print("哈哈")
#
# def func3():
#     print("哈哈")
#
# lst = [func1,func2,func3]
# for i in lst:
#     i()


# 4.函数名可以作为参数传递给函数
# def my():
#     print("我是my")
#
# def proxy(fn):
#     fn() #执行传递过来的my
# proxy(my)
#过程:首先调用proxy函数,将my参数传递给proxy,然后变成proxy(my),然后调用my()
    #接着打印"我是my"

#函数名可以作为参数进行传递(多层嵌套)
# def func():
#     print("我是func")
# def func1():
#     print("我是func1")
#
# def func2(fn):
#     print("我是func2")
#     fn()
# func2(func1)  #结果:我是func2,我是func1
#解释:首先执行func2函数,然后有个实参传递给形参,打印我是func2,然后执行func1()函数打印我是func1

#例二:
# def func():
#     print("我是func")
# def func1():
#     print("我是func1")
#
# def func2(fn,gn):
#     print("我是func2")
#     fn()
#     gn()
#     print("hahaha")
# func2(func1,func)
# 我是func2
# 我是func1
# 我是func
# hahaha
#解释:首先执行func2函数,有两个实参传递给形参,打印我是func2,接着执行func1函数打印我是func1,
    #接着执行funch函数打印我是func,在打印hahaha


# 5.函数名可以作为函数的返回值
# def func():
#     print("我是func")
#     a = 10 # 变量
#     def inner():
#         print("我是inner")
#     return inner  #得出inner,然后使用inner()调用
# print(func)
# ret = func()
# ret()
# func()() #先运行func() 然后再返回值上加()

操作例子

六.函数的嵌套

1.主要遇见()就是函数被调用了,如果没有()就不是函数的调用
2.函数的执行顺序

#函数嵌套例子

#例子:
# def fun():
#     print(111)
# def fun1():
#     print(222)
#     fun()
# fun1()
# print(111)
#结果是222,111,111
#解释:定义函数,然后最先调用的是fun1这个函数,所以先的打印fun1中的内容,
    #接着再调用fun()函数,再打印111,接着再打印111

#例二
# def fun1():
#     print("蒋小雨")
#     def fun2():
#         print("鲁炎")
#     def fun3():
#         print("张冲")
#         def fun4():
#             print("龙大队")
#             fun2()
#         fun4()
#     fun3()
# def fun5():
#     print("二哈")
#     fun1()
# fun5()
#二哈,蒋小雨,张冲,龙大队,鲁炎
#分析:首先调用fun5,打印二哈,接着调用fun1,打印蒋小雨,然后调用fun3的函数,打印张冲
    #接着执行下面函数体代码块,调用fun4,打印龙大队,接着调用fun2打印鲁炎

嵌套例子

七.命名空间

把存放名字和值的关系的空间叫做命名空间

#命名空间分类

1.全局命名空间:在py文件中,函数外声明的变量都属于全局命名空间
2.局部命名空间:在函数中声明的变量会放在局部命名空i教案
3.内置命名空间:存放python解释器为我们提供的名字
           如:list,tuple,str,int这些就是内置命名空间

#取值顺序

1.局部命名空间
2.全局命名空间
3.内置命名空间

#取值顺序例子:
a = 10 #全局命名空间
def func():
    a = 20 #局部命名空间
    print(a)
func() #20

八.作用域

定义:作用域就是作用的范围,按照生效范围分为:全局作用域和局部作用域全局作用域:包含内置命名空间和全局命名空间,在整个文件都可以使用      可以通过globals()函数来查看全局作用域中的内容局部作用域:在函数内部可以使用      可以通过locals()函数来查看局部作用域中的变量和函数信息

#例子:

#例子
# a = 10
# def func():
#     a = 40
#     b = 20
#     def abc():
#         print("哈哈")
#     print(a,b)  #这里使用的是局部作用域
#     print(globals()) #打印全局作用域中的内容
#     print(locals())  #打印局部作用域中的内容
# func()

#关键字global和nonlocal讲解

global:更改全局变量中的值
    理解:在局部中定义一个局部变量,然后加了global,就会将全局中定义的变量的值改成局部的那个变量的值

#global的应用
#例子
# a = 10
# def func():
#     global a
#     a += 10
#     print(a)
# func() #20 ,加了global就可以改变外部的值了,如果不加是不能更改的

#例如:不加global更改全局参数的时候就会报错
# a = 10
# def func1():
#     a += 10
#     print(a)
# func1()
#总结点:全局变量本身就是不安全的,不能随意修改


nonlocal:寻找外层函数中离他最近的那个变量
#例子:
# a = 10
# def func1():
#     a = 20
#     def func2():
#         nonlocal a
#         a = 30
#         print(a)
#     func2()
#     print(a)
# func1()
# print(a) #30,30,10
#nonlocal是更改离他最近的那个变量,所以,将上一个a=20,改为30
#所以打印是30,30,10,因为nonlocal将20改成了30

详解

九.闭包

定义:在内层函数中访问外层函数的变量

闭包的作用:
    1.可以保护变量不受侵害
    2.可以让一个变量常驻内存

#例子:

#作用例子:
#1、保护变量不受侵害
#首先举一个全局变量不安全的例子
# a = 10
# def outer():
#     global a
#     a = 20
#     print(a)
#
# def outer_2():
#     global a
#     a = 30
#     print(a)
#
#
# outer()
# outer_2()
#得出结果是20,30
#解释:首先调用outer()函数更改为20,然后再调用outer_2函数打印30
#       这样就会出现哪个先调用就执行那个,所以改来改去是很混乱的

# def outer():
#     a = 10  #这个变量对外界是不开放的
#     def func():
#         nonlocal a  #寻找外层函数中离他最近的那个进行修改
#         a = 20
#         print(a)
#     func()
# outer()
# 
# def outer_2(): #这个函数不能对a = 10进行修改
#     pass

#2、让一个变量常驻内存
# def outer():
#     a = 10  #常驻内存,为了inner执行的时候有值,因为你不知什么时候调用
#     def inner():
#         print(a)
#     return inner
# fn = outer()
# print("大大大")
# print("笑笑笑")
#
# fn() #相当于inner(),调用inner函数


#使用 _closure_ 查看函数是不是闭包

#例子:不是闭包的检测
# def outer():
#     def func():
#         print("我不是闭包")
#     print(func.__closure__)  #None
# outer()

#例二:是闭包
# def outer():
#     a = 10
#     def func():
#         print(a)
#     print(func.__closure__)
# outer()  #(<cell at 0x000001B7B3E7D978: int object at 0x00007FF97124B470>,)

#结论:如果打印的是None,不是闭包,如果不是None,就是闭包

作用例子

十.迭代器

#迭代器
#   可以简单理解为:通用的去遍历某个对象的方式

#有些数据类型是可迭代的,有些是不可迭代的,如果使用不可迭代的来进行循环就会报错

#例子:使用不可迭代对象来进行循环就会报错
# s = 123
# for i in s:
#     print(i)
#这样打印会报错: TypeError: ‘int‘ object is not iterable:数字不是一个可迭代对象

#那么问题就来了,怎么知道是不是一个可迭代对象呢?
#可以通过dir查看xx类型的数据可以执行哪些方法
# print(dir(str))  #__iter__ iterable(可迭代)
# print(dir(list)) #__iter__
# print(dir(int))    #如果没有__iter__,说明不是可迭代对象,不是可迭代对象那么相对应的就不能进行循环

#结论:所有的带__iter__是可以使用for循环的,是可迭代对象

#可迭代对象就可以使用__iter__()来获取到迭代器
#迭代器里面有__next__()
# s = "我喜欢看火蓝刀锋"
# it = s.__iter__() #获取迭代器
# print(dir(it))   #迭代器里有__iter__ 还有__next__

#迭代器的特点

1.只能向前取,下一个下一个,不能往回
2.几乎不占用内存,可以有效节省内存
3.for循环
4.惰性机制

#例子:
1.只能向前拿
#print(it.__next__()) #我
# print(it.__next__()) #喜
# print(it.__next__()) #欢
# print(it.__next__()) #看
# print(it.__next__()) #火

2.迭代器模拟for循环
# lst = ["蒋小雨","张冲","鲁炎","龙大队"]
# for el in lst: #底层使用的是迭代器
#     print(el)

特点

#判断数据是否可迭代

#例子:
# lst = ["张冲","鲁炎","蒋小雨"]

#it = lst.__iter__()

# print("__iter__" in dir(it))  #True
# print("__next__" in dir(it))  #True
# print(list(it))
#通过dir来判断数据是否可迭代的,以及数据是否是迭代器
#
# #官方方案
# from collections.abc import Iterable #可迭代对象
# from collections.abc import Iterator  #迭代器
#
# print(isinstance(lst,Iterable))  #True
# print(isinstance(lst,Iterator))  #False lst列表本身不是迭代器

十一.生成器

1.生成器的本质就是迭代器,和迭代器的特点一样,取值方式和迭代器一样(__next__(),send()
2.在python种有三种方式来获生成器
    1.通过生成器函数
    2.通过各种推导式来实现生成器
    3.通过数据的转换也可以获取生成器

生成器函数

1.函数中如果由yield函数就是生成器函数
2.生成器函数在执行的时候,默认不会执行函数体,会返回生成器
3.yield:相当于return可以返回数据,但是yield不会彻底中断函数,会分段执行函数

#例子:不执行函数体,拿到的是生成器
# def func():
#     print(‘哈哈‘)
#     yield 1
#     print(‘呵呵呵‘)
# gen = func() #这样子你就会发现不会执行你的函数,拿到的是生成器,如果是return的话就会执行函数了
# print(gen.__next__()) #这样子就会执行函数,执行到下一个yield,就是说执行看到yield就结束

#生成器应用

#应用场景
# 比如你喜欢吃鸡蛋,设想你去市场可以一下子就买一万个,这样也是可以,但是有个问题就是鸡蛋久了就会烂,存放是个问题,这样就会很浪费,但是如果你买了个鸡回来,就可以解决掉存放空间问题,想什么时候吃就拿一个

#例子:一下子买一万个浪费
# def egg():
#     lst = []
#     for i in range(10000):
#         lst.append(‘鸡蛋‘+str(i))
#         return lst
# ll = egg() #一下子买10000个就会很占用内存

#例二:买只鸡,生鸡蛋,想吃一个就拿一个
# def egg():
#     for i in range(10000):
#         yield ‘鸡蛋‘+str(i)
# g = egg() #获取生成器
# sir = g.__next__()
# print(sir) #鸡蛋0
# sir1 = g.__next__()
# print(sir1) #鸡蛋1
#这样子就是想吃一个就拿一个

#这样子也验证了生成器的3个特点
#   1.惰性机制,拿一个才给你取一个
#   2.省内存
#   3.只能向前拿

应用场景

#send()方法

send()和__next__()是一样的,可以执行到下一个yield,可以给上一个yield位置传值

#send和__next__()区别:
    1.send和next()都是让生成器走下一次
    2.send可以给上一个yield的位置传递值,不能给最后一个yield发送值,在第一次执行生成器代码的时候不能使用send()

#使用send给上一个yield传值例子:

#例子:使用send给上一个yield传值
def func():
    print("我吃什么啊")
    a = yield "玉米"  #这里的a和后面的yield是没关联的,会把传进来的值交给print执行
    print("a=",a)
    b = yield "饺子"
    print("b=",b)
    c = yield "包子"
    print("c=",c)
    yield "OVER"  #最后收尾的一定是yield,不然会有报错

g = func() #获取生成器,记住有yield的是生成器函数,只会给你获取到生成器,不会执行函数体
ret1 = g.__next__() #没有上一个yield,所以不能使用send(),开头必须__next__()
print(ret1)
ret2 = g.send("大饼")
print(ret2) #a=大饼
ret3 = g.send("粥")
print(ret3) #b=粥
ret4 = g.send("冰淇淋")
print(ret4) #c=冰淇淋
#解析:执行时候首先我吃什么啊,然后打印玉米,接着ret2使用send上一个yield传值,所以就变成a=大饼,接着打印a=大饼
#       要注意变量和右边的yield是两段来的,互不相干,接着打印饺子,ret3使用send给上一个yield传值,变成b=粥,接着继续执行

传值例子

#生成器可以使用for循环来获取内部的元素

#为什么生成器可以使用for循环呢,因为生成器实质就是迭代器
#例子:
def func():
    print(111)
    yield 222
    print(333)
    yield4444
    print(555)
    yield 666
for i in func():
    print(i)

十二.推导式

1.推导式:就是使用一句话来生成
2.包括:列表推导式,字典推导式,集合推导式,
3.注意点:没有元组推导式
4.3种推导式的语法:
    1.列表推导式:[结果 for循环 条件判断]
    2.字典推导式:{k:v for循环 条件判断}
    3.集合推导式:{k for循环 条件判断}

#3种推导式的应用

#1.列表推导式
        语法: [结果 for循环 判断语句]

#例子:首先我们先来一个打印一年级到12年级,我们可能想到的是定义一个空列表,然后把元素追加到列表里面
# lst = []
# for i in range(1,13):
#     lst.append("年级"+str(i))
# print(lst)

#如果使用推导式的话,那么就是使用一句话来生成一个列表
# lst = ["年级"+str(i) for i in range(1,13)]
# print(lst)  #和上面列表追加一样的效果

#例二:使用推导式取1-100的奇数
# lst = [i for i in range(100) if i%2 == 1]
# print(lst)

#例三:寻找名字中带有两个e的人的名字
# names = [[‘Tom‘,‘tomi‘,‘findall‘,‘Wesley‘,‘Steven‘,‘Jon‘],[‘Alice‘,‘Ana‘,‘Jennifer‘,‘Eva‘]]
#逻辑:首先打开第一层列表,拿到下列表,然后再到小列表里面拿元素,再进行统计判断

#使用推导式写
# lst = [name for line in names for name in line if name.count(‘e‘) ==2]
# print(lst) #[‘Wesley‘, ‘Steven‘, ‘Jennifer‘]

#使用常规算法写
# lst = []
# for line in names:
#     for name in line:
#         if name.count(‘e‘) == 2:
#             lst.append(name)
# print(lst) #[‘Wesley‘, ‘Steven‘, ‘Jennifer‘]


#2.字典推导式
        语法:{key:value for循环 条件判断}

#例子:将列表的元素转换成字典,转换形式:[11,22,33,44] => {0:11,1:22,2:33}
# lst = [11,22,33,44]
# dic = {i:lst[i] for i in range(len(lst))}
# print(dic) #{0: 11, 1: 22, 2: 33, 3: 44}

#例二:将字典的key和value进行调换
#   思路:首先先拿到key和value,然后在推导式的结果那里掉不同位置就可以了
# dic = {"zs":"赵四","ln":"刘能","zc":"张冲","ly":"鲁炎"}
# d = {v:k for k,v in dic.items()}
# print(d) #{‘赵四‘: ‘zs‘, ‘刘能‘: ‘ln‘, ‘张冲‘: ‘zc‘, ‘鲁炎‘: ‘ly‘}


#3.集合推导式
#要记住集合的特点:去重,无序,元素必须式可哈希不可变的数据类型
#例1:使用集合推导式去重复
# s = {i for i in range(100)}
# print(s)

#例二:去重复
# lst= [1,2,3,4,2,1,3,4,6,7]
# s = {el for el in lst}
# print(s) #{1, 2, 3, 4, 6, 7}

推导式应用

 

十三.生成器表达式

1.生成器表达式可以直接获取到生成器对象,生成器对象可以直接进行for循环,生成器具有惰性机制
2.生成器表达式语法:
        (结果 for 变量 in 可迭代对象 if 条件判断)

#生成器表达式应用

#下面将演示生成器的最大点特,惰性机制,要拿才给你拿一个,拿走了就没有值了
# def func():
#     print(111)
#     yield 222
#     yield 333
#
# a = func() #获取到生成器
# a1 = (i for i in a)  #生成器
# a2 = (i for i in a1) #生成器
# print(list(a)) #[222, 333]
# print(list(a1)) #[]
# print(list(a2)) #[]
#分析:为什么前面的a有值,后面的都没有值了?
#解:因为a是源头,他从源头把数据给拿走了,所以后面再从前面拿的话就不会有值了,这就验证了生成器的惰性机制,你拿一个才给你一个,拿走了就没有了

#那么后面还能不能获取到值? 答案是可以的,可以再做一个源头,再拿数据
#例如:再定义一个a3获取生成器,再进行调用,这样子a2就有值了
# def func():
#     print(111)
#     yield 222
#     yield 333
#
# a = func() #获取到生成器
# a1 = (i for i in a)  #生成器
# a3 = func()
# a2 = (i for i in a3) #生成器
# print(list(a)) #[222, 333]
# print(list(a1)) #[]
# print(list(a2)) #[222, 333] #

应用

#面试题

#题目:计算拿到的值是多少
#求和函数
# def add(a,b):
#     return  a + b

#生成器函数
# def test():
#     for r_i in range(0,4):
#         yield r_i
#
# g = test() #获取到生成器
#
# for n in [2,10]:
#     g = (add(n,i) for i in g)

#上的for可以看成循环量词
# for n in [2]:
#     g = (add(n,i) for i in g)
# for n in [10]:
#     g = (add(n,i) for i in g)
#因为是2还没取值,所以是叠加进去,将g换成上面
#  g = (add(n,i) for i in (add(n,i) for i in 0,1,2,3)

# print(list(g)) #20,21,22,23
#分析:第一个函数是来求和得,第二个函数是生成器函数,没有打印值,然后到for循环,可以想象是2和10都执行了一次,但是因为生成器得惰性机制,然后执行2是没有值,所以是不关2的事情,执行10得时候才会执行,将10带进去算
#最后的执行是这样:g = (add(10,i) for i in (add(10,i) for i in 0,1,2,3),所以就成10+10,10+11,10+12,10+13

#提示:惰性机制,不到最后是不会拿值得


#下面接着演示列表有多个值,但是它只会看最后面的那个,前面的会相加
# def add(a,b):
#     return  a + b

#生成器函数
# def test():
#     for r_i in range(0,4):
#         yield r_i
# g = test() #获取到生成器
# 
# for n in [1,3,7]:
#     g = (add(n,i) for i in g)
#这个就是相当于叠加了3次
# g = (add(7,i) for i in (add(7,i) for i in (add(7,i) for i in 0,1,2,3)

# print(list(g))  #21,22,23,24
#最后的运算是:(add(7,i) for i in (add(7,i) for i in (add(7,i) for i in 0,1,2,3) #将7带进去运算,把最后那个带进去算
#首先是0+7,1+7,2+7,3+7,接着7+7,7+8,7+9,7+10,然后7+14,7+15,7+16,7+17

面试题及讲解

#生成器表达和列表推导式的区别

1.生成器表达式比较省内存,列表推导式比较耗内存
2.得到的值不一样,列表推导式得到的是一个列表,生成器表达式获取的是生成器

相关推荐