一.函数名的运用
函数名是一个变量,但它是一个特殊的变量,与括号配合可以执行函数的变量.
1.函数名的内存地址
1 deffunc():2 print("呵呵")3 print(func)4 结果:5 function func at 0x1101e4e
2.函数名可以赋值给其他变量
1 deffunc():2 print("呵呵")3 print(func)4 a = func #把函数当成一个变量赋值给另一个变量
5 a() #函数调用 func()
3.函数名可以当做容器类的元素
1 deffunc1():2 print("呵呵")3 deffunc2():4 print("呵呵")5 deffunc3():6 print("呵呵")7 deffunc4():8 print("呵呵")9 lst =[func1, func2, func3]10 for i inlst:11 i()
4.函数名可以当做函数的参数
1 deffunc():2 print("吃了么")3 deffunc2(fn):4 print("我是func2")5 fn() #执行传递过来的fn
6 print("我是func2")7 func2(func) #把函数func当成参数传递给func2的参数.
5.函数名可以作为函数的返回值
1 deffunc_1():2 print("这里是函数1")3 deffunc_2():4 print("这里是函数2")5 print("这里是函数1")6 returnfunc_27 fn = func_1() #执行函数1. 函数1返回的是函数2, 这时fn指向的就是上面函数2
8 fn() #执行上面返回的函数
二.闭包
什么是闭包?闭包就是内层函数,对外层函数(非全局)的变量的引用.叫闭包
1 deffunc1():2 name = "alex"
3 deffunc2():4 print(name) #闭包
5 func2()6 func1()7 结果:8 alex
我们可以使用__closure__来检测函数是否是闭包.使用函数名.__closure__返回cell就是闭包.返回None就不是闭包.
1 deffunc1():2 name = "alex"
3 deffunc2():4 print(name) #闭包
5 func2()6 print(func2.__closure__) #(
7 #0x10c3fc650>,)
8 func1()
问题,如何在函数外边调用内部函数呢?
1 defouter():2 name = "alex"
3 #内部函数
4 definner():5 print(name)6 returninner7
8 fn = outer() #访问外部函数, 获取到内部函数的函数地址
9 fn() #访问内部函数
那如果多层嵌套呢?很简单,只需要一层一层的往外层返回就行了
1 deffunc1():2 deffunc2():3 deffunc3():4 print("嘿嘿")5 returnfunc36 returnfunc27
8 func1()()()
由它我们可以引出闭包的好处.由于我们在外界可以访问内部函数,那这个时候内部函数访问的时间和时机就不一定了,因为在外部,我可以选择在任意时间去访问内部函数.这个时候,想一想,我们之前说过,如果一个函数执行完毕.则这个函数中的变量以及局部命名空间中的内容都会被销毁.在闭包中,如果变量被销毁了,那内部函数将不能正常执行.所以,python规定.如果你在内部函数中访问了外层函数中的变量.那么这个变量将不会消亡.将会常驻在内存中,也就是说.使用闭包,可以保证外层函数中的变量在内存中常驻.这样做有什么好处呢?非常大的好处.我们来看一个爬虫代码:
1 from urllib.request importurlopen2 defbut():3 content =urlopen("http://www.xiaohua100.cn/index.html").read()4 defget_content():5 returncontent6 returnget_conte7 fn = but() #这个时候就开始加载校花100的内容
8 #后面需要用到这里面的内容就不需要在执行非常耗时的网络连接操作了
9 content = fn() #获取内容
10 print(content)11 content2 = fn() #重新获取内容
12 print(content2)
综上,闭包的作用就是让变量能够常驻内存.供后面的程序使用
三.迭代器
什么叫做迭代器?迭代器英文意思是iterator。
1 l = [1,2,3,4]2 l_iter = l.__iter__() #将可迭代的转化成迭代器
3 item = l_iter.__next__()4 print(item)5 item = l_iter.__next__()6 print(item)7 item = l_iter.__next__()8 print(item)9 item = l_iter.__next__()10 print(item)11 item = l_iter.__next__()12 print(item)
迭代器遵循迭代器协议:必须拥有__iter__方法和__next__方法。
for循环,能遍历一个可迭代对象,他的内部到底进行了什么?
将可迭代对象转化成迭代器。(可迭代对象.__iter__())
内部使用__next__方法,一个一个取值。
加了异常处理功能,取值到底后自动停止。
用while循环模拟for循环:
1 l = [1,2,3,4]2 l_iter = l.__iter__()3 whileTrue:4 try:5 item = l_iter.__next__()6 print(item)7 exceptStopIteration:8 break
为什么要有for循环?
基于上面讲的列表这一大堆遍历方式,聪明的你立马看除了端倪,于是你不知死活大声喊道,你这不逗我玩呢么,有了下标的访问方式,我可以这样遍历一个列表啊
1 =[1,2,3]2
3 index=04 while index
7
8 #要毛线for循环,要毛线可迭代,要毛线迭代器
没错,序列类型字符串,列表,元组都有下标,你用上述的方式访问,perfect!但是你可曾想过非序列类型像字典,集合,文件对象的感受,所以嘛,年轻人,for循环就是基于迭代器协议提供了一个统一的可以遍历所有对象的方法,即在遍历之前,先调用对象的__iter__方法将其转换成一个迭代器,然后使用迭代器协议去实现循环访问,这样所有的对象就都可以通过for循环来遍历了,而且你看到的效果也确实如此,这就是无所不能的for循环,最重要的一点,转化成迭代器,在循环时,同一时刻在内存中只出现一条数据,极大限度的节省了内存~
四.总结
Iterable:可迭代对象.内部包含__iter__()函数
Iterator:迭代器.内部包含__iter__()同时包含__next__().
迭代器的特点:
1.节省内存.
2.惰性机制
3.不能反复,只能向下执行.
我们可以把要迭代的内容当成子弹.然后呢,获取到迭代器__iter__(),就把子弹都装在弹夹中.然后发射就是__next__()把每一个子弹(元素)打出来.也就是说,for循环的时候,一开始的时候是__iter__()来获取迭代器.后面每次获取的元素都是通过__next__()来完成的.当程序遇到StopIteration将结束循环.