python学习028-----python之函数(四):内嵌函数、闭包及global、nonlocal关键字

1.global关键字
global关键字可以在局部作用域内访问全局变量。当没有加global关键字时,局部作用域内可以访问全局变量。但如果尝试去修改全局变量的话,python会使用屏蔽(Shadowing)的方式来保护全局变量。即:当在函数内部试图修改全局变量时,python会在函数内部创建一个名字一模一样的局部变量。这样,修改只会修改到函数内部的局部变量,而不会影响到全局变量。举例:

count = 5
def Fun():
    count = 10
    return count

Fun()
print(count)   

运行结果:
当把上面的代码加上了global关键字时,全局变量count便能被修改了:

count = 5
def Fun1():
    global count
    count = 10
    return count

Fun1()
print(count)  

运行结果:
关于屏蔽,更详细的例子见下面[闭包]的第二个例子

2.内嵌函数(内部函数)
内部函数不能直接被调用,要首先调用它的上级函数,例略。

3.闭包(closure)
如果在一个内部函数里,对外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就会被认为是闭包。简单地说:内部函数引用外部参数(非全局变量),则称这个内部函数为闭包。闭包的使用方法要注意,具体见下例:

------------------------------------------------ ex1 ------------------------------------------------

def FunX(a):
    def FunY(b):
        return a * b
    return FunY  #注意:这里FunY没有括号,下面才能用双括号法调用内部函数!(x = FunX(6)(5))详见思考与练习第2题。
#======================================================================================
#这其中,FunY()使用了非全局变量外部函数a,则称FunY()为一个闭包。
#注意闭包的使用:
#======================================================================================
i = FunX(6)
print(i)   #【输出】:<function FunX.<locals>.FunY at 0x0000000001E5D840>
print(type(i))  #【输出】:<class 'function'>
#根据上面两行的输出情况,说明i仍然是个函数。想要正确输出,应该把
#内嵌函数的参数也加上:
x = i(5)
print(x)  #【输出】:30
#或者也可以这么写:
x = FunX(6)(5)
print(x)  #【输出】:30
#注意:越过外部函数,直接调用内部函数是非法的。

------------------------------------------------ ex2 ------------------------------------------------

#=====================================这是一段错误代码=====================================
def Fun1():
    x = 5
    def Fun2():
        x = x*x
        return x
    return Fun2()

a = Fun1()
print(a)

上面的代码,看似会输出25,实际上会报错:UnboundLocalError: local variable 'x' referenced before assignment。即局部变量x不能在没有定义前就使用。产生这个错误的原因是因为x = 5定义在Fun2()的外部,对于Fun2()来说是全局变量。由本文开头知
Fun()中访问x是可以的,但此处对x进行了修改,引发了python的屏蔽,故报错。解决方法:
【方法一】
python3之前是没有直接的解决办法的,只能简介地通过容器类型来解决。原因是因为列表不是存放在栈中的,不会导致屏蔽。

def Fun1():
    x = [5]
    def Fun2():
        x[0] = x[0]*x[0]
        return x[0]
    return Fun2()

a = Fun1()
print(a)   

运行结果:
【方法二】
python3中可以使用【nonlocal】关键字来解决上面的问题。使用方法与global类似。【nonlocal用于在内部函数中修改外部函数的局部变量】,要注意:nonlocal不能用来访问全局变量!
【global与nonlocal区别】:
1.global用于在函数中修改全局变量的值
2.nonlocal用于内部函数中修改外部函数的局部变量
3.global可以修改全局变量的值,nonlocal不会影响到全局变量的值,但可以改变外部函数的局部变量的值
4.global可以对未定义变量使用,nonlocal只能对已定义变量使用

def Fun1():
    x = 5
    def Fun2():
        nonlocal x
        x = x*x
        return x
    return Fun2()

a = Fun1()
print(a)  

运行结果:
4.思考与练习
1).为什么下面A代码运行正常,B代码却报错?
-------------------------代码A-------------------------

def outside():
    var = 5
    def inside():
        var = 3
        print(var)
    inside()

outside()

-------------------------代码B-------------------------

def outside():
    var = 5
    def inside():
        print(var)
        var = 3
    inside()

outside()

【原因】:仔细看代码B的报错内容:#UnboundLocalError: local variable 'var' referenced before assignment,即:var没有在使用前定义。问题的核心在于"屏蔽"。由于outside()已经有了一个var变量,所以inside()中的var变量是把outside()的var变量屏蔽后的新变量。故此时在内层函数中是无法访问到外层函数的var变量的。故A运行正确,B报错。
2.如何访问内部函数?
1)可以直接调用外部函数的情况外部函数返回值为内部函数

def outside():
    def inside():
        print('I am inside')
    return inside()

outside()

2)要用双括号法的情况外部函数返回值为内部函数的函数名

def outside():
    def inside():
        print('I am inside')
    return inside

outside()()

3)在2)的情况下,这么做也是可以的:

def outside():
    def inside():
        print('I am inside')
    return inside

go = outside()
go()

3.下面是一个闭包的例子,请判断输出结果。

def funX():
    x = 5
    def funY():
        nonlocal x
        x = x + 1
        return x

    return funY

a = funX()
print(a())
print(a())
print(a())

运行结果:
粗略一看,感觉会输出6 6 6,因为每次运行funY()时,x都会重新初始化成5,然后x=x+1,x为6。但是!实际上是输出6 7 8,为什么 x 没有每次都初始化呢?实际上是因为a = funX()是没有改变的,执行a()也就是在执行funY()函数,也就是说,只要a没有被重新赋值,funX()就没有被释放,也就是说局部变量x就没有被重新初始化,程序实际上一直在执行funY()函数,funX()函数一直没有结束运行。所以输出为:6 7 8。所以当全局变量不适用的时候,用闭包更稳定安全。

4.编写一个程序,可以统计你输入的字符串中各字符的数量

str1 = input('请输入一个字符串:')
list1 = []

for each in str1:
    if each not in list1: #统计过的就不用重新统计了,所以是not in
        if each == '\n':
            print('空格', str1.count(each))
        else:
            print(each, str1.count(each))
        list1.append(each)

5.寻找密码:
找出输入的字符串中的密码,该密码符合以下规律:
a) 每位密码为单个小写字母
b) 每位密码的左右两边有且只有三个大写字母

str1 = input('请输入一个字符串:')
countA = 0                #位于密码左侧的大写字母数
countB = 0                #连续三个大写字母后的小写字母数
countC = 0                #位于密码右侧的大写字母数
length = len(str1)
for i in range(length):   #循环 遍历字符串中的所有字符:
    if str1[i] == '\n':   #     如果 该字符是空格:
        continue          #          直接进行下一次循环
    if str1[i].isupper(): #     如果 该字符是大写字母:
        if countB == 1:   #          如果 三个连续大写字母后的小写字母数为1:
            countC += 1   #               右侧大写字母数+1    
            countA = 0    #               左侧大写字母数归零
        else:             #          否则:
            countA += 1   #               左侧大写字母数+1
        continue          #          直接进行下一次循环
    if str1[i].islower() and countA == 3:
                          #     如果 该字符是小写字母,且左侧大写字母数为3:
        countB = 1        #          小写字母数+1
        countA = 0        #          左侧大写字母数清零
        target = i        #          记录该字符的索引
        continue          #          直接进行下一次循环
    if str1[i].islower() and countC == 3:
                          #     如果 该字符是小写字母,且右侧大写字母数为3:
        print(str1[target], end = ' ')
                          #          输出该字符
    countA = 0            #     清零计数器,以便进行下一次循环
    countB = 0
    countC = 0

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值