美文网首页
python函数进阶——函数名,闭包,迭代器

python函数进阶——函数名,闭包,迭代器

作者: hiDaLao | 来源:发表于2018-08-21 15:23 被阅读19次

    函数名的应用


    我们都知道,函数名的命名规则与变量基本相同,那么函数名可以做那些事呢?

    首先,函数是通过 函数名()调用的,那么如果直接输出函数名会是什么情况呢?

    def func():
        return 666
    print(func())
    print(func)
    
    666
    <function func at 0x0000021AC539AD90>
    

      可以看到,如果直接输出函数名,那么打印的是函数在内存中的地址,所以函数名的第一个用法是:
      函数名表示函数的内存地址

    同时,函数名不仅命名方式与变量相同,函数名还可以直接当作变量使用
    见以下代码:

    def func1():
        return 666
    c = func
    c()
    
    666
    

      不仅如此,函数名还可以作为函数的参数以及返回值
      见下:

    def func2(a):
        print(a)
        return a()
        
    def func3():
        print(666)
    
    func2(func3)
    
    <function func3 at 0x0000021AC4785158>
    666
    

      最后,函数名还可以作为容器类类型的元素

    a = 6
    b = 4
    c = 6
    l = [a,c,c]
    print(l)
    
    def func4():
        print(666)
    
    def func5():
        print(777)
    
    def func6():
        print(888)
    l1 = [func4,func5,func6]
    for i in l1:
        i()
        
        
    
    [6, 6, 6]
    666
    777
    888
    

      以上便是函数名的应用,总结一下,函数名共有以下作用:

    • 函数名可以表示函数的内存地址
    • 函数名可以作为变量使用
    • 函数名可以作为函数的参数和返回值
    • 函数名可以作为容器类类型的元素

    闭包


      什么是函数的闭包?首先是首先让我们看看维基百科中关于闭包的概念:

      在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。闭包可以用来在一个函数与一组“私有”变量之间创建关联关系。在给定函数被多次调用的过程中,这些私有变量能够保持其持久性
      通过这个定义,我们可以得出函数中形成闭包需要满足以下条件:

    • 必须要有函数的嵌套
    • 内层函数必须要引用外层函数中的变量(不能是全局变量)
    • 外层函数必须返回内部函数的引用
    def func():     #定义一个外部函数
        x = 10
        def func7():     #定义一个内部函数
            nonlocal x
            x += 1      #内部函数中引用外部函数
            return x
        return func7    #外部函数返回内部函数
    
    f = func()          # f = func7
    print(f())
    print(f())
    print(f())
    print(x)
    
    11
    12
    13
    
    
    
    ---------------------------------------------------------------------------
    
    NameError                                 Traceback (most recent call last)
    
    <ipython-input-27-0478bb8088ab> in <module>()
         11 print(f())
         12 print(f())
    ---> 13 print(x)
    
    
    NameError: name 'x' is not defined
    

      那么闭包有什么作用呢?

       当程序执行时,遇到了函数执行,他会在内存中开辟一个空间,局部名称空间,如果这个函数内部形成了闭包,那么他就不会随着函数的结束而消失。

       something more:closure方法

      闭包函数相对于普通函数会多出一个closure属性里面定义了一个元组用于存放所有的cell对象,每个cell对象一一保存了这个闭包中所有的外部变量。

    def func8():
        pass
    print(func8.__closure__)
    
    def func9():
        i = 10
        j = 'yang'
        def func10():
            print(i,j)
        print(func10.__closure__)
        return func10
    l = func9()
    l()
    
    None
    (<cell at 0x0000021AC5447EB8: int object at 0x0000000056A96200>, <cell at 0x0000021AC5447498: str object at 0x0000021AC5471F10>)
    10 yang
    

    可迭代对象和迭代器


    首先抛出结论:

    • 可迭代对象包含迭代器。
    • 如果一个对象拥有iter方法,其是可迭代对象;如果一个对象拥有next方法,其是迭代器。
    • 定义可迭代对象,必须实现iter方法;定义迭代器,必须实现iter和next方法。

      在介绍迭代器之前,我们先来回顾一下之前提到的一个名词:可迭代对象(iterable)
      在之前的的介绍中,我们知道字符串、列表、字典等都是可迭代对象,我们可以对可迭代对象进行for循环,如下:

    s = '123'
    li = [1,2,3]
    dic = {1:2,2:3,3:4}
    for i in s:
        print(i)
    for i in li:
        print(i)
    for i in dic:
        print(i)
    for i in 123:             #报错信息提示:'int' object is not iterable int型不可迭代
        print(i)
    
    1
    2
    3
    1
    2
    3
    1
    2
    3
    
    
    
    ---------------------------------------------------------------------------
    
    TypeError                                 Traceback (most recent call last)
    
    <ipython-input-36-9241d29cd0b3> in <module>()
          8 for i in dic:
          9     print(i)
    ---> 10 for i in 123:
         11     print(i)
    
    
    TypeError: 'int' object is not iterable
    

    那么,究竟什么是可迭代对象呢?在理解这个概念之前,我们必须先了解一个方法:__iter__
    对象内部含有__iter__方法就是可迭代对象.我们通过代码来验证:

    a = 123
    s = '123'
    l = [1,2,3]
    dic = {1:2,2:3,3:4}
    print('__iter__' in dir(a))          #dir方法可以获得调用对象的所有属性和方法
    print('__iter__' in dir(s))
    print('__iter__' in dir(l))
    print('__iter__' in dir(dic))
    
    False
    True
    True
    True
    

    判断对象是不是可迭代对象的方法除了iter方法外,还有isinstance方法:

    from collections import Iterable
    print(isinstance(a,Iterable))
    print(isinstance(s,Iterable))
    print(isinstance(l,Iterable))
    print(isinstance(dic,Iterable))
    
    False
    True
    True
    True
    

      那么什么是迭代器呢?在最开始的结论中我们提到,迭代器属于可迭代对象,但比可迭代对象多了一个__next__方法,那么问题来了,what's __next__方法?手动黑人问号脸
      最常见的迭代器是文件句柄,见下:

    f = open('register', encoding='utf-8')
    print('__iter__' in dir(f))
    print('__next__' in dir(f))
    print('__iter__' in dir(dict))
    print('__next__' in dir(dict))
    
    True
    True
    True
    False
    

    那么__next__方法如何使用呢?每next一次,取一次值

    print(f.__next__())
    print(f.__next__())
    
    yang|123
    
    jin|456
    

    可迭代对象与迭代器的区别:

    • 可迭代对象不能取值,迭代器是可以取值的。(不借助索引或key等方法)
    • 迭代器非常节省内存。
    • 迭代器每次只会取一个值。
    • 迭代器单向的,一条路走到头。
      可迭代对象是可以通过__iter__()转为迭代器的
    lis = [1, 2, 3]  # 可迭代对象
    ite1 = lis.__iter__()  # 迭代器  <list_iterator object at 0x0000027A183BFFD0>
    ite1 = iter(lis)  # 迭代器  <list_iterator object at 0x0000027A183BFFD0>
    print(ite1)
    
    <list_iterator object at 0x0000021AC5499588>
    

    for循环对可迭代对象进行遍历的背后实现原理:

    1. 将可迭代对象转化成迭代器。
    2. 调用__next__方法取值。
    3. 利用异常处理停止报错。
    
    while 1:
        try:
            print(ite1.__next__())
        except StopIteration:
            break
    
    1
    2
    3
    

    相关文章

      网友评论

          本文标题:python函数进阶——函数名,闭包,迭代器

          本文链接:https://www.haomeiwen.com/subject/pkkjiftx.html