# 19. 函数名,闭包,迭代器
# 函数名的运用
函数名也是一个变量,但它也是一个特殊的变量,和括号配合可以执行函数的变量
这样说,函数名中的值也不是实际的值,只是存储了这些值所在的内存地址
# 函数名的内存地址
如果不用括号的话,那就会打印出内存地址
def so():
print("人生的意义")
print(so)
print(so())
执行结果:
<function so at 0x00000162176E1EA0>
人生的意义
None
以上实例,可以看得出,如果不加括号的结果,和加了括号的结果,下面的None,很熟悉了吧,这是函数的默认返回 值,因为使用print()这个函数使用函数,会默认调用函数的返回值
# 函数名赋值变量
可以通过赋值的方式,让变量接收到函数中的内存地址,从而能调用函数
def so():
print("人生的意义")
wo = so
wo()
执行结果:
人生的意义
以上实例,也可以这样理解,在使用print()函数执行函数的时候不加括号,这样就会得到函数的内存地址,上面的变量 赋值也是这样的原理,拿到函数的内存地址赋值到变量中,这样变量就可以像函数一样调用函数,函数名==变量
# 函数名可以当做容器类型的元素使用
函数名可以当成变量在容器类型中使用
def qo():
print("人生的意义")
def wo():
print("方向在那里")
def eo():
print("是不是每次的选择都代表的以后方向")
def ro():
print("如果是,人一生中会遇到无数中选择")
## 第一种方式
so = [qo,wo,eo,ro]
print(so)
## 第二种方式
so = [qo(),wo(),eo(),ro()]
print(so)
## 第三种方式
so = [qo,wo,eo,ro]
for i in so:
i()
执行结果:
## 第一种执行结果
[<function qo at 0x000002926BFC1EA0>, <function wo at 0x000002926C19BA60>, <function eo at 0x000002926C19BD08>, <function ro at 0x000002926C19BD90>]
## 第二种执行结果
人生的意义
方向在那里
是不是每次的选择都代表的以后方向
如果是,人一生中会遇到无数中选择
[None, None, None, None]
## 第三种执行结果
人生的意义
方向在那里
是不是每次的选择都代表的以后方向
如果是,人一生中会遇到无数中选择
以上实例:
1. 第一种方式:使用列表并将函数放在里面不加括号,这样子使用print()函数只会输出函数的内存地址
2. 第二种方式:使用列表并将函数放在里面加括号,这样子使用print()函数只会输出函数的返回值,那为什么会有函数的数据,因为Python解释器在调用函数时会先执行一遍。
3. 第三种方式:可以使用for循环,每次循环到的函数在加括号执行一遍
# 函数可以当做函数的参数
函数是可以当成参数给另一个函数的值,函数 == 变量
def wo():
print("方向在那里")
def so(wo):
print("人生的意义")
wo()
so(wo)
执行结果:
人生的意义
方向在那里
以上实例,也没什么可以说的了,这种写法很少人写,遇到的话,很幸运
# 函数名作为函数的返回值
函数是可以作为返回值,返回给调用者,函数 == 变量
def so():
print("方向在那里")
def wo():
print("人生的意义")
return wo()
so()
执行结果:
方向在那里
人生的意义
以上实例, 很强调了函数也是属于变量的一种,如果返回值能返回变量,那为什么不能返回函数呢
# 闭包
闭包就是内层函数对外层函数(非全局)的变量的引用
可以让一个局部变量常驻内存
闭包可以保护一个变量,不被修改污染
# 闭包
def so():
se = "人生的阳光灿烂的日子"
def eo():
print(se)
eo()
so()
执行结果:
人生的阳光灿烂的日子
以上实例,闭包只要是内层函数对外层函数变量的调用就是闭包,非全局
# 通过内置函数查看函数是否是闭包
可以通过 closure 来检测函数是否是闭包
如果不是闭包返回:None
def so():
se = "人生的阳光灿烂的日子"
def eo():
print(se)
eo()
print(eo.__closure__)
so()
执行结果:
人生的阳光灿烂的日子
(<cell at 0x00000264B1766648: str object at 0x00000264B17E0810>,)
以上实例,如果是闭包就返回一堆值,如果不是就返回None
# 在函数外部调用内部函数的变量
def so():
se = "人生的阳光灿烂的日子"
def eo():
print(se)
return eo
wo = so()
wo()
执行结果:
人生的阳光灿烂的日子
以上实例,为什么在函数执行完毕变量会没被删除,之前文章中也写到了,函数执行完毕,内部数据会被删除,也差不 多这意思,在这里要补充一点,如果Python解释器在执行函数的时候发现内部变量可能有外部调用,那Python解释器默 认会把这变量常驻内存
# 在函数外部调用内部函数的变量(多层嵌套)
def so():
se = "人生的阳光灿烂的日子"
def eo():
def ro():
print(se)
print(se)
return ro
return eo
wo = so()()
wo()
执行结果:
人生的阳光灿烂的日子
人生的阳光灿烂的日子
# 关于闭包的好处-纯文字-可略过
在外部可以访问内部函数,这时候内部函数访问的时间和时机就不一定,因为外部可以选择任意的时间去访问内部函数,想一下,之前文章写过的,一个函数执行完毕,则这个函数中的变量以及局部命名空间中的内容都会被删除,在闭包中,如果变量被删除,寻内部函数将不能正常执行,所以Python内部规定,如果在内部函数中访问了外层函数中的变量,那么这个就是将不会被删除,还会常驻内存中,也是说,使用闭包可以保证外层函数中的变量在内存中常驻
闭包的作用就是让⼀个变量能够常驻内存. 供后面的程序使用
# 一个简单的爬虫的代码
from urllib.request import urlopen
def so():
en = urlopen("http://92.linux91.cn/index.php/archives/111/").read()
def wo():
return en
return wo
ss = so()
en = ss()
print(en)
es = ss()
print(es)
执行结果:
执行结果我就不写出来了,太长了
代码行意:
引用urllib模块中的urlopen函数
创建一个函数
通过urlopen爬取指定网址的信息,并读取到内存中,赋值给变量
创建一个函数
设置一个返回值,返回上层的变量
设置一个返回值,返回下层函数
将函数赋值给变量,赋值内存地址
将获取到的内容赋值给一个变量,为什么不是内存地址,因为赋值要先把函数执行一遍
使用print()执行变量
在次获取到的内容赋值给一个变量,为什么不是内存地址,因为赋值要先把函数执行一遍
使用print()执行变量
# 迭代器
之前文件一直都有写到可迭代对象进行迭代操作,那么到底什么才是可迭代对象
可迭代对象:字符串,列表,元组,字典,集合
为什么可以称为可迭代对象呢,因为他们都遵循了可迭代协议
# 使用内置函数查看是否可迭代对象 - dir()
怎么知道数据或数据类型是不是可迭代对象,就要使用dir()是否包含__iter__ 函数的方法
iter 函数称为:iterable表示可迭代的,表示可迭代协议
so = "你好呀,今天天气真不错"
print(dir(so))
执行结果:
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
以上实例,就要从中找到是否包含__iter__ 函数
# 整形
so = 1111
print("__iter__" in dir(so))
执行结果:
False
以上实例,整形中的方法没有__iter__ 函数
# 字符串
so = "你好呀,今天天气真不错"
print("__iter__" in dir(so))
执行结果:
True
以上实例,字符串中的方法有__iter__ 函数
# 列表
so = [1,"你好",2,"今天天气怎么样"]
print("__iter__" in dir(so))
执行结果:
True
以上实例,列表中的方法有__iter__ 函数
# 元组
so = (1,"你好",2,"今天天气怎么样")
print("__iter__" in dir(so))
执行结果:
True
以上实例,元组中的方法有__iter__ 函数
# 字典
so = {1:"真不错",2:"你确实吗"}
print("__iter__" in dir(so))
执行结果:
True
以上实例,字典中的方法有__iter__ 函数
# 集合
so = {"哈喽","您好"}
print("__iter__" in dir(so))
执行结果:
True
以上实例,集合中的方法有__iter__ 函数
# 查看文件操作是否为可迭代对象
so = open("文件",mode="r",encoding="utf-8")
wo = so.read()
print("__iter__" in dir(wo))
执行结果:
True
上实例,文件操作中的方法有__iter__ 函数
# 还可以使用另一种方式来查看 - isinstence()
可以通过isinstence()函数加上iterable来查看对象是否可迭代对象
可以通过isinstence()函数加上iterator来查看对象是否是迭代器
Iterable: 可迭代对象. 内部包含__iter__()函数
Iterator: 迭代器. 内部包含__iter__() 同时包含__next__()
from collections import Iterable
from collections import Iterator
so = [1,2,3,4,5,6,7,8]
li = so.__iter__()
print(isinstance(so,Iterable))
print(isinstance(so,Iterator))
print(isinstance(li,Iterable))
print(isinstance(li,Iterator))
执行结果:
True
False
True
True
以上实例,查看so跟li变量是否是可迭代对象跟迭代器
可以确定. 如果对象中有__iter__函数. 那么我们认为这个对象遵守了可迭代协议. 就可以获取到相应的迭代器. 这里的__iter__是帮助我们获取到对象的迭代器
# 使用内置迭代器来获取内容 - next
使用__next__来获取__iter__中的内容
so = "今天天气怎么样"
li = so.__iter__()
print(li.__next__())
print(li.__next__())
print(li.__next__())
print(li.__next__())
print(li.__next__())
print(li.__next__())
print(li.__next__())
执行结果:
今
天
天
气
怎
么
样
以上实例,如果使用__next__来获取一旦获取超过值的话会报错
so = ""
li = so.__iter__()
print(li.__next__())
执行结果:
StopIteration
以上实例,就是__next__获取不到值后报错StopIteration
# 模拟for循环机制
so = "今天天气怎么样"
li = so.__iter__()
while 1:
try:
it = li.__next__()
print(it)
except StopIteration:
break
执行结果:
今
天
天
气
怎
么
样
代码行义解意
创建一个字符串变量
加载可迭代对象函数并赋值给变量
while循环-无限
获取元素:
加载迭代器并赋值给变量
打印变量
如果遇到StopIteration这个错误:
退出循环
# 迭代器总结 - 全文本 - 可略过
Iterable: 可迭代对象. 内部包含__iter__()函数
Iterator: 迭代器. 内部包含__iter__() 同时包含__next__()
迭代器特点:
节省内存
- 惰性机制(如果不去取值,它就不会返回值)
- 不能反复,只能向下执行
可以这样理解,迭代的内容是子弹,iter()就是弹夹,next()就是枪
把子弹装入弹夹中,在用枪一个子弹(获取到的值)一一打出
for循环的时候,也是一开始就是用__iter__()来获取迭代器的,后面每次获取元素都是通过__next__()来完成的,当程度遇到StopIteration错误,就结束循环