函数的三大器
迭代器
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 fn fn = 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 fn return 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 content return 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 Iterator print(isinstance(l,Iterable)) # True print(isinstance(l,Iterator)) # False print(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将结束循环