Python笔记 Day8(闭包和迭代器)

一、函数名的运用:(函数名其实就是一个变量名)

  当def定义后的函数名加上括号就可以调用函数了具体函数名有哪些运用哪?(注意函数的命名规则是和变量名的命名规则一样的)

  1、函数名的内存地址:

def func():
    print("呵呵") 

print(func)   # 结果: <function func at 0x1101e4ea0> 就是一个变量

  2、函数名可以赋值给其他变量(就像其他变量的赋值)

def func():
    print("呵呵")

 print(func) 
a = func    # 把函数当成⼀个变量赋值给另⼀个变量
a()    # 函数调func()

  3、函数名可以当做容器类元素

def func1():
    print("呵呵")
def func2():
    print("呵呵") 
def func3():
    print("呵呵")
def func4():
    print("呵呵")

lst = [func1, func2, func3]   #作为列表的元素储存,然后分别遍历
for i in lst:
    i()

  4、函数名可以作为另一个函数的参数:

def func():
    print("吃了么")
def func2(fn):
    print("我是func2")
    fn()    # 执行传递过来的fn
    print("我是func2") 

func2(func)     # 把函数func当成参数传递给func2的参数fn.

  5、函数名可以作为函数的返回值

def func_1():
    print("这里是函数1")
    def func_2():
        print("这⾥是函数2")
    print("这⾥是函数1")
    return func_2 

fn = func_1()   # 执行函数1.  函数1返回的是函数2, 这时fn指向的就是上⾯函数2 
fn()    # 执行上⾯返回的函数


 def func():
            def inner():
                pass
            return inner

 func()()    # 效果于上一个一样

考点:
def fun():
print('1')
def inner():
print('3')
return inner

print(fun()()) #运行结果1, 3, None 因为在调用内层函数时,没有返回值,所以千万别忘了None
ret = fun() #将return的函数地址赋值给ret,将内层的函数加载到全局作用域中
ret() #在全局作用域中调用内层函数
print(ret()) # 1, 3, None



inner() # 注意单独调用这一行是会报错的,因为是内层的函数的临时空间,全局不认识 NameError: name 'inner' is not defined

二、闭包(  什么是闭包?  闭包就是内层函数, 对外层函数(非全局)的变量量的引⽤, 叫闭包    主要是和全局变量作区别 

def func1():
    name = "小珠"
    def func2():
        print(name)     # 闭包    凡是调用了不是全局变量的的过程,那么就称闭包
    func2() 

func1()    #结果: 小珠  


考试题:

name = '老男孩'
def wraaper2(n):
    #  n = '老男孩' 相当于
    def inner():
        print(n)
    inner()
    print(inner.__closure__)  #  None
wraaper2(name)                     # 这也是一个闭包,虽然name是一个全局变量但是waaper2中引用了n
扫描二维码关注公众号,回复: 2697604 查看本文章

  1、我们可以使用__closure__来检测函数是否是闭包,使用函数名.__closure__返回cell就是闭包,返回None就不是闭包

def func1():
    name = "小珠"
    def func2():
        print(name)     # 闭包
    func2()
    print(func2.__closure__)    # (<cell at 0x10c2e20a8: str object at 0x10c3fc650>,)    一般在和要检验的函数平行的位置进行检验

func1()

  2、如何在函数外边调用多层内部函数哪?

def func1():
    def func2():
        def func3():
            print("嘿嘿")
        return func3
    return func2

 func1()()()  # 嘿嘿  对于多层嵌套,只需要一层层剥

  3、 由它我们可以引出闭包的好处.  由于我们在外界可以访问内部函数. 那这个时候内部函数访问的时间和时机就不⼀定了, 因为在外部, 我可以选择在任意的时间去访问内部函数. 这 个时候. 想一想. 我们之前说过, 如果一个函数执行完毕. 则这个函数中的变量以及局部命名 空间中的内容都将会被销毁.  在闭包中. 如果变量被销毁了. 那内部函数将不能正常执⾏. 所 以. python规定. 如果你在内部函数中访问了外层函数中的变量. 那么这个变量将不会消亡. 将会常驻在内存中. 也就是说.使用闭包, 可以保证外层函数中的变量在内存中常驻. 这样做有什么好处呢? 非常⼤的好处. 我们来看一个关于爬⾍的代码

   总结:1、保护你的变量不受外界影响

      2、可以让变量常驻内层(因为不确定内层函数什么时候调用外层的变量,所以会常驻内存)

from urllib.request import urlopen 
def but():
    content = urlopen("http://www.xiaohua100.cn/index.html").read()
    def get_content():
        return content     #感觉多此一步,实则是为了不让content不轻易消失
    return get_content

fn = but()  # 这个时候就开始加载校花100的内容 # 后⾯需要用到这⾥面的内容就不需要在执行⾮常耗时的网络连接操作了 
content = fn()  # 获取内容 
print(content) 

content2 = fn()  # 重新获取内容 
print(content2)

三、迭代器
    1、我们之前一直在⽤用可迭代对象进⾏迭代操作. 那么到底什么是可迭代对象. 本小节主要讨 论可迭代对象. ⾸首先我们先回顾⼀下⽬前我们所熟知的可迭代对象有哪些:
    str, list, tuple, dict, set. 那为什么我们可以称他们为可迭代对象呢? 因为他们都遵循了可迭代协议. 什么是可迭代协议. ⾸先我们先看⼀段错误代码
# 对的 
s = "abc" 
for c in s:
    print(c) 

# 错的 
for i in 123:    
    print(i) 

# 结果: Traceback (most recent call last): File "/Users/sylar/PycharmProjects/oldboy/iterator.py", line 8, in <module> for i in 123: TypeError: 'int' object is not iterable 注意看报错信息中有这样一句句话. 'int' object is not iterable . 翻译过来就是整数类型对象是不可迭代的. iterable表⽰可迭代的. 表⽰可迭代协议.
那么如何进行验证你的数据类型是否符合可迭代协议. 我们可以通过dir函数来查看类中定义好的所有⽅法.

     2、使用dir来查看该数据包含了那些方法用来遍历列表,字符串,元祖....可迭代对象

    可迭代对象: Iterable, 里面有__iter__()可以获取迭代器, 没有__next__()

    代器: Iterator, 里面有__iter__()可以获取迭代器, 还有__next__()

s = "我的哈哈哈" 
print(dir(s))       # 可以打印对象中的方法和函数 print(dir(str))     # 也可以打印类中声明的方法和函数

在打印结果中. 寻找__iter__ 如果能找到. 那么这个类的对象就是一个可迭代对象. 

['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', 

'__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__',

'__iter__', '__le__', '__len__', '__lt__', '__mod__'……

我们发现list,tuple,dict,set都有__iter__功能,说明他们都是可迭代对象!

     3、目前有两种方式可以检测一个对象是否是可迭代对象:

                            3.1、土方法:

lst = '我是你大帅哥'

print('__iter__' in dir(lst))   #True
print('__next__' in dir(lst))   #False

it = lst.__iter__()    #将lst转化为迭代器赋值给it
print('__iter__' in dir(it))   #True   迭代器本来就是迭代对象
print('__iter__' in dir(it))  #True  迭代器拥有next方法可进行循环

       3.2、高富帅方法:以通过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,Iterator)) #True
print(isinstance(l_iter,Iterable)) #True

     4、(for循环的原理)综上. 我们可以确定. 如果对象中有__iter__函数. 那么我们认为这个对象遵守了可迭代协议. 就可以获取到相应的迭代器. 这里的__iter__是帮助我们获取到对象的迭代器.

我们使⽤迭代 器中的__next__()来获取到一个迭代器中的元素.  那么我们之前讲的for的⼯作原理到底是什 么? 继续看代码

s = "我爱北京天安门" 
c = s.__iter__()    # 获取迭代器 
print(c.__next__())     # 使用迭代器器进行迭代. 获取⼀个元素  我 
print(c.__next__())     #
print(c.__next__())     #
print(c.__next__())     #
print(c.__next__())     #
print(c.__next__())     #
print(c.__next__())     #
print(c.__next__())     # StopIteration 


for循环的内部机制:
for i in [1,2,3]:
    print(i)    使用while循环+迭代器来模拟for循环(必须要掌握)

lst = [1,2,3] 
lst_iter = lst.__iter__()    #获取迭代器
while True:
    try:    #试试
        i = lst_iter.__next__()   #调用下一个元素
        print(i)
    except StopIteration:    #除了事我兜着
        break     
 

 总结迭代器特点:

    1. 节省内存.

            2. 惰性机制

            3. 不能反复, 只能向下执行. 

  我们可以把要迭代的内容当成子弹. 然后呢. 获取到迭代器__iter__(), 就把子弹都装在弹夹中.  然后发射就是__next__()把每⼀个子弹(元素)打出来. 也就是说, for循环的时候

. ⼀开始的时候是__iter__()来获取迭代器. 后⾯每次获取元素都是通过__next__()来完成的. 当程序遇到 StopIteration将结束循环.!   

  

猜你喜欢

转载自www.cnblogs.com/double-W/p/9459982.html