迭代器
1.函数名的使用以及第一类对象
2.闭包
3.迭代器
一.函数名的运用
函数名是一个变量,但它是一个特殊的变量,与括号配合可以执行函数的变量
1.函数名的内存地址
def func():print('呵呵') print(func)
# 结果: # <function func at 0x0000000000502E18>
2.函数名可以赋值给其他变量
def func():print('呵呵')a = func a()# 结果: # 呵呵
3.函数名可以当做容器类的元素
def func1():print('呵呵111') def func2():print('呵呵222') def func3():print('呵呵333') def func4():print('呵呵444')li = [func1,func2,func3,func4] for func in li:func()# 结果: ''' 呵呵111 呵呵222 呵呵333 呵呵444 '''
4.函数名可以当做函数的参数
def func():print('你是谁?我是猪八戒啊')def fn(s):print('谁在我里面,谁在说话?')s()print('哇,不好了,连我也中招了')fn(func)# 结果 ''' 谁在我里面,谁在说话? 你是谁?我是猪八戒啊 哇,不好了,连我也中招了 '''
5.函数名可以作为函数的返回值
def func():print('我是func ')def fn():print('我是fn')print('我是func')return fnfn = func() fn()# 结果 ''' 我是func 我是func 我是fn'''
二 .闭包
什么是闭包?闭包就是内层函数,对外层函数(非全局) 的变量的引用
def fuc():name = 'hahage'def foo():print(name)foo() fuc() # 结果: # hahage
可以使用__closure__ 来检测函数是否是闭包,使用函数名 .__closure__ 返回 cell就是 闭包,返回None不是闭包
def func():name = '哈哈哥'def foo():print(name)foo()print(foo.__closure__) func()# 结果: # 哈哈哥 # (<cell at 0x00000000026F7D98: str object at 0x00000000021EAA50>,)
问题 ---- 如何在函数外边调用内部函数呢?
def outer():name = '哈哈哥'def foo():print(name)return foo foo = outer() # 访问外部函数,获取到内部函数的函数地址 foo() # 访问内部函数# 结果 # 哈哈哥
那如果多层嵌套呢? 很简单 --- 只需要一层一层的往外层返回就行了
def func():def foo():def fn():print('嘿嘿嘿')return fnreturn foo foo = func() fn = foo() fn() # 结果 # 嘿嘿嘿
由它可以引出闭包的好处,由于我们在外界可以访问内部函数,那这个时候内部函数访问的时间和时机就不一样了,因为在外部,在任意时间访问内部函数,这个时候,想一想,我们之前说过,如果一个函数执行完毕,则这个函数中的变量以及局部命名空间中的内容都将会被销毁,在闭包内,如果变量被销毁了,那内部的函数将不能正常执行,所以,python规定,如果你在内部函数中访问了外层函数的变量,那么这个变量将不会消亡,,,将会常驻内存中,也就是说,使用闭包,可以保证外层函数中的变量在内存中常驻,这样做有什么好处?非常大的好处,我们来看一个关于爬虫的代码:
from urllib.request import urlopen def but():content = urlopen('http://www.521609.com/').read()def get_content():return contentreturn get_content fn = but() content = fn() print(content)content2 = fn() print(content2)
综上; 闭包的作用就是让一个变量能够常驻内存,供后面的程序使用
三 . 迭代器
我们之前一直在用可迭代对象进行迭代操作,那么到底什么是可迭代对象,本小节主要讨论可迭代对象,首先我们先回顾一下目前我们所熟知的可迭代对象有哪些?
str,list,tuple,dict,set ,那为什么我们可以称他们为可迭代对象呢?因为他们都遵循了可迭代协议,什么是可迭代协议?首先看一段代码
# 对的 s = 'abc' for c in s:print(c)# 错的 for i in 123:print(i) # 结果: ''' a b c Traceback (most recent call last):File "F:/爬虫/函数.py", line 28, in <module>for i in 123: TypeError: 'int' object is not iterable
'''
注意看报错信息中有这样一句话"'int' object is not iterable" 翻译过来就是整数类型对象是不可迭代的,iterable表示可迭代的,表示可迭代协议,那么如何进行验证你的数据类型是否符合可迭代协议,我们可以通过dir函数来查看类中定义好的所有方法
s = '我是哈哈哥' print(dir(s)) # 打印对象中的方法和函数 print(dir(str)) # 打印类中声明的方法和函数''' ['__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'] ['__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__ 如果能找到,那么这个类的对象就是一个可迭代对象
我们发现字符串中可以找到 __iter__ ,list,tuple,dict,set ,range 都有 __iter__ ,这几个数据类型同时也可以进行 for循环
这是查看一个对象是否是可迭代对象的第一种办法,我们还可以通过 isinstence() 函数来查看对象是什么类型的
l = [1,2,3] l_iter = l.__iter__() from collections import Iterable from collections import Iteratorprint(isinstance(l,Iterable)) # True print(isinstance(l,Iterator)) # Falseprint(isinstance(l_iter,Iterable)) # True print(isinstance(l_iter,Iterator)) # True
综上 ----- 我们可以确定,如果对象中有 __iter__函数,那么我们认为这个对象遵循了 可迭代协议,就可以获取到相对应的迭代器,这里的 __iter__ 是帮帮助我们获取到对象的迭代器,我们使用迭代器中的 __next__( 来获取到一个迭代器中的元素,那么我们之前讲的 for 循环 的工作原理到底是什么,还是看下面的代码 ----------
s = '我是哈哈哥' c = s.__iter__() # 获取迭代器 print(c.__next__()) # 使用迭代器进行迭代 ,获取一个元素 我 print(c.__next__()) # 是 print(c.__next__()) # 哈 print(c.__next__()) # 哈 print(c.__next__()) # 哥
for 循环的机制
for i in [1,2,3]:print(i)
使用 while 循环 + 迭代器来模拟 for 循环
lst = [1,2,3] lst_iter = lst.__iter__()while 1:try:i = lst_iter.__next__()print(i)except StopIteration:break
总结:
iterable:可迭代对象,内部包含 __iter__ () 函数
iterator : 迭代器 -- 内部包含__iter__() 同时包含 __next__()
迭代器的特点:
1.节省内存
2.惰性机制
3.不能反复,只能向下执行
我们可以把要迭代的内容当成子弹,然后呢?获取到迭代器 __iter__( ),就是把子弹都装在弹夹中,然后发射就是 __next__() 把每一个子弹(元素)打出来,也就是说,for 循环的时候,一开始的时候是 __iter__()来获取迭代器,后面每次取元素都是通过 __next__()来完成的,当程序遇到StopIteration将结束循环