• 020,函数:内嵌函数与闭包


    020,函数:内嵌函数与闭包

     
    回顾: 
    >>> count = 5        #全局变量
    >>> def MyFun():
        count = 10            #局部变量
        print(10)
     
        
    >>> MyFun()            #读取局部变量
    10
    >>> print(count)        #打印全局变量(等于是屏蔽了局部变量)
    5
    >>> 
     
     global关键字 
    >>> def MyFun():
        global count            #告诉编译器把这个count变成全局变量
        count = 10
        print(10)
     
        
    >>> MyFun()
    10
    >>> print(count)
    10
    >>> 
     
    内嵌函数(允许在函数内部创建另一个函数,叫内部函数) 
    >>> def fun1():
        print('fun1()')
        def fun2():
            print('fun2()')
        fun2()
     
        
    >>> fun1()
    fun1()
    fun2()
    >>> fun2()
    Traceback (most recent call last):
      File "<pyshell#18>", line 1, in <module>
        fun2()
    NameError: name 'fun2' is not defined
    >>> 
    注意:内部函数的整个作用域都在外部函数之内,如fun2整个定义和调用的过程都在fun1里面,除了在fun1里可以任意调用,出了fun1之外无法被调用,会系统报错

    闭包——函数式编程的重要语法结构。
    如果在一个内部函数被外部作用域(但不是全局作用域的变量)进行引用,那么内部函数会被认为是闭包 
    >>> def funX(x):    #  x
        def funY(y):    #
            return x * y      #x
        return funY
     
    >>> i = funX(8)      #类似直接调用funX(8)      
    >>> i
    <function funX.<locals>.funY at 0x022E5738>
    >>> type(i)
    <class 'function'>
    >>> i(5)            #等同 (funX(8))(5)
    40
    >>> funX(8)(5)
    40
    >>> 
     
    报错例子: 
    >>> def fun1():
        x = 5
        def fun2():
            x *= x
            return x
        return fun2()
     
    >>> fun1()
    Traceback (most recent call last):
      File "<pyshell#45>", line 1, in <module>
        fun1()
      File "<pyshell#44>", line 6, in fun1
        return fun2()
      File "<pyshell#44>", line 4, in fun2
        x *= x
    UnboundLocalError: local variable 'x' referenced before assignment   #这里错误提示,在fun2()里,x在没有被赋值之前不能被引用。
    >>>
     改造:
     在python2.x里可以投机取巧用列表实现x的调用 
    >>> def fun1():
        x = [5]
        def fun2():
            x[0] *= x[0]
            return x[0]
        return fun2()
     
    >>> fun1()
    25
    >>> 
     在python3.x中,用关键字:nonlocal 
    >>> def fun1():
        x = 5
        def fun2():
            nonlocal x
            x *= x
            return x
        return fun2()
     
    >>> fun1()
    25
    >>> 
     

    测试题:
          
    0. 如果希望在函数中修改全局变量的值,应该使用什么关键字?
    答:global
     
    >>> count = 5
    >>> def MyFun():
                    global count
                    count = 10
                    print(count)
     
    >>> MyFun()
    10
    >>> count
    10
     


    1. 在嵌套的函数中,如果希望在内部函数修改外部函数的局部变量,应该使用什么关键字?
    答: nonlocal 
    举个例子:
    1. >>> def Fun1():
    2.                 x = 5
    3.                 def Fun2():
    4.                         nonlocal x
    5.                         x *= x
    6.                         return x
    7.                 return Fun2()
    8. >>> Fun1()
    9. 25

    2. Python的函数可以嵌套,但要注意访问的作用域问题哦,请问以下代码存在什么问题呢?
    1. def outside():
    2.     print('I am outside!')
    3.     def inside():
    4.         print('I am inside!')
    5. inside()
       答:inside()是内部函数,不能在外部调用
    使用嵌套函数要注意一点就是作用域问题,inside()函数是内嵌在outside()函数中的,所以inside()是人妻,除了身为老公的outside()可以碰(调用),在外边或者别的函数体里是无法对其进行调用的。(这老师真邪恶)

    3. 请问为什么代码A没有报错,但代码B却报错了?应该如何修改?

    代码A:
    1. def outside():
    2.     var = 5
    3.     def inside():
    4.         var = 3
    5.         print(var)
    6.         
    7.     inside()
    8. outside()
    复制代码
    代码B:
    1. def outside():
    2.     var = 5
    3.     def inside():
    4.         print(var)
    5.         var = 3
    6.         
    7.     inside()
    8. outside()
    答:UnboundLocalError: local variable 'var' referenced before assignment   代码B中 在inside()里先执行print(var)语句,这是var是外部变量,没有在inside()里定义,故报错。
    仔细一看报错的内容是:UnboundLocalError: local variable 'var' referenced before assignment,说的是变量var没有被定义就拿来使用,肯定错啦!

    这里outside()函数里有一个var变量,但要注意的是,内嵌函数inside()也有一个同名的变量,Python为了保护变量的作用域,故将outside()的var变量屏蔽起来,因此此时是无法访问到外层的var变量的。

    应该修改为:
    1. def outside():
    2.     var = 5
    3.     def inside():
    4.         nonlocal var
    5.         print(var)
    6.         var = 8
    7.         
    8.     inside()
    9. outside()

    4. 请问如何访问funIn()呢?
    1. def funOut():
    2.     def funIn():
    3.         print('宾果!你成功访问到我啦!')
    4.     return funIn()
    答:funOut()

    5. 请问如何访问funIn()呢?
    1. def funOut():
    2.     def funIn():
    3.         print('宾果!你成功访问到我啦!')
    4.     return funIn
     答:funOut()()

    6. 以下是“闭包”的一个例子,请你目测下会打印什么内容?
    1. def funX():
    2.     x = 5
    3.     def funY():
    4.         nonlocal x
    5.         x += 1
    6.         return x
    7.     return funY
    8. a = funX()
    9. print(a())
    10. print(a())
    11. print(a())
     答:不在idle测试,目测 
    6
    7
    8
    有些鱼油可能会比较疑惑,这……怎么跟全局变量一样了?局部变量x不是应该在每次调用的时候都重新初始化了吗?!

    其实大家仔细看看就明白了,当a = funX()的时候,只要a变量没有被重新赋值,funX()就没有被释放,也就是说局部变量x就没有被重新初始化。

    所以当全局变量不适用的时候,可以考虑使用闭包更稳定和安全,你还可以参考 -> 游戏中的角色移动:闭包在实际开发中的作用 

    动动手:(这个比较难,先复习之前的知识过后才着手练习。)
         
    0. 请用已学过的知识编写程序,统计下边这个长字符串中各个字符出现的次数并找到小甲鱼送给大家的一句话。
    (由于我们还没有学习到文件读取方法,大家下载后拷贝过去即可) 
    答:前面 定义了一个变量叫 txt = 文件里的所有字符
     
    def count():
        #
        symbols = r'''`!@#$%^&*()_+-=/*{}[]|'";:/?,.<>'''
        letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
        #symbols
        for each in symbols:
            #txt            
            if each in txt:
                num = txt.count(each)
                print(' %s %d ' % (each,num))
        #
        for each2 in txt:      #for each2 in lettersletters  
            if each2 in letters:
                print(each2,end = '')
     
    count()
     论坛参考答案: 
    str1 = ''''''
    list1 = []
     
    for each in str1:
        if each not in list1:
            if each == ' ':
                print('\n', str1.count(each))
            else:
                print(each, str1.count(each))
            list1.append(each)
     不明觉厉!!!!
     


    1. 请用已学过的知识编写程序,找出小甲鱼藏在下边这个长字符串中的密码,密码的埋藏点符合以下规律:
        a) 每位密码为单个小写字母
        b) 每位密码的左右两边均有且只有三个大写字母
    (由于我们还没有学习到文件读取方法,大家下载后拷贝过去即可) 
     答:前面 定义了一个变量叫 txt = 文件里的所有字符 ------------我居然没用到函数。。。。
    length = len(txt)
    for i in range(length):
        if txt[i].islower() and txt[(i+1):(i+4)].isupper() and txt[(i-3):(i)].isupper() :
            print(txt[i],end = '')
     
    论坛参考答案:
    1. str1 = '''拷贝过来的字符串'''
    2. countA = 0
    3. countB = 0
    4. countC = 0
    5. length = len(str1)
    6. for i in range(length):
    7.     if str1[i] == ' ':
    8.         continue
    9.     if str1[i].isupper():
    10.         if countB == 1:
    11.             countC += 1
    12.             countA = 0
    13.         else:
    14.             countA += 1
    15.         continue
    16.     if str1[i].islower() and countA == 3:
    17.         countB = 1
    18.         countA = 0
    19.         target = i
    20.         continue
    21.     if str1[i].islower() and countC == 3:
    22.         print(str1[target], end='')
    23.     countA = 0
    24.     countB = 0
    25.     countC = 0
     
     
     总结,我怎么觉得操作题自己写的代码比论坛参考答案还简洁一些呢?!
  • 相关阅读:
    js在字符串中提取数字
    checkbox简单例子
    递归算法及经典递归例子代码实现
    e
    MyEclipse10中启动出现OutOfMemoryError: PermGen space如何解决
    VB php JAVA关于数据库连接数过多的解决方法
    java.lang.UnsupportedClassVersionError: Bad version number in .class file
    KindEditor得不到textarea值的解决方法
    php创建读取 word.doc文档
    php 在web端读出pdf 与各种文件下载
  • 原文地址:https://www.cnblogs.com/fishdm/p/3574069.html
Copyright © 2020-2023  润新知