• Python之路Python全局变量与局部变量、函数多层嵌套、函数递归


    Python之路Python全局变量与局部变量、函数多层嵌套、函数递归

    一、局部变量与全局变量

     

    1、在子程序中定义的变量称为局部变量,在程序的一开始定义的变量称为全局变量。全局变量作用域是整个程序,局部变量作用域是定义该变量的子程序。

    全局变量没有任何缩进,在任何位置都可以调用。

     

    子程序:如用def定义的函数。

     

    作用域

    一个标识符的可见范围,这就是标识符的作用域。一般常说的是变量的作用域

    全局作用域(global):在整个程序运行环境中都可见

    局部作用域:在函数、类等内部可见;局部变量使用范围不能超过其所在的局部作用域。

     

    例子

    NAME = "nicholas"
    def change_NAME():
        print("change_NAME", NAME)
    change_NAME()
    print(NAME)

     


    输出结果

    change_NAME nicholas
    nicholas

     


    分析:NAME = "nicholas"就是全局变量,在
    change_NAME()函数体内可以直接调用打印出“change_NAME nicholas”

     

    2、当全局变量与局部变量同名时:


    在定义局部变量的子程序内,局部变量起作用;在其它地方全局变量起作用。

    例子:

     

    NAME = "nicholas"
    def change_NAME():
        NAME = "niubi"
        print("change_NAME", NAME)
    change_NAME()
    print(NAME)

     

      

    输出结果

    change_NAME niubi
    nicholas

     

    分析:当全局变量与局部变量同名时:在 def change_NAME():函数内部,
    执行print("change_NAME", NAME)语句时,这里的NAME优先调用函数内部的值,函数执行结束后执行print(NAME)语句,全局变量NAME = "nicholas"起作用。


    3、如果函数内部无global关键字


    优先读取局部变量,如果没有局部变量则读取全局变量,此时无法对全局变量进行赋值。

    但是对于可变对象可以对内部元素进行操作(如append()pop()).

     

    大前提:无global关键字

    a、有声明(同名)局部变量
    例子

    name = ["pony","jack"]
    print(1,name)
    def change_name():
        name = "nicholas"
        print("change_name", name)
    change_name()
    print(2,name)


    输出结果

    1 ['pony', 'jack']
    change_name nicholas
    2 ['pony', 'jack']
    1 ['pony', 'jack']
    change_name nicholas
    2 ['pony', 'jack']

     

      

    分析:这里无golbal关键字,执行 print(1,name)语句时读取全局变量name = ["pony","jack"],之后执行change_name()函数,在函数里面nema被赋值为"nicholas"
    执行print("change_name", name)语句,这里的name优先读取函数内部的局部变量name = "nicholas"
    输出change_name nicholas。之后change_name()函数结束。
    执行print(2,name)语句。这里仍然读取全局变量name = ["pony","jack"]。


    b、无声明(同名)局部变量
    例子

    name = ["pony","jack"]
    print(1,name)
    def change_name():
        print(3, name)
        name.append("nicholas")
        print(4,name)
    change_name()
    print(2,name)

     

      


    输出结果

    1 ['pony', 'jack']
    3 ['pony', 'jack']
    4 ['pony', 'jack', 'nicholas']
    2 ['pony', 'jack', 'nicholas']

     

      


    分析:无global关键字,针对全局变量如果是可变对象,可以对内部元素进行操作。

     

    4、如果函数中有global关键字,变量本质上就是全局变量,可读取可赋值。

     

    a、有声明(同名)局部变量

    例子

    NAME = "nicholas"
    print(1,NAME)
    def change_NAME():
        global NAME
        NAME = "niubi"
        print("change_NAME", NAME)
    change_NAME()
    print(2,NAME)

     

      

    输出结果

    1 nicholas
    change_NAME niubi
    2 niubi

     

      

    分析:在执行print("1",NAME)语句时,NAME使用全局变量,然后执行change_NAME()函数,在函数内部有global关键词声明,之后执行NAME = "niubi",执行完这句之后整个程序的NAME变量的值就被修改为"niubi"。
    继续输出change_NAME niubi,函数结束。

    最后执行print("2",NAME)语句,由于NAME在函数内部被修改为"niubi",所以这里输出
    2 niubi

     


    例子2

    name = ["pony","jack"]
    print(1,name)
    def change_name():
        global name
        name = ["nick"]
        name.append("nicholas")
        print(3,name)
    change_name()
    print(2,name)

     



    输出结果

    1 ['pony', 'jack']
    3 ['nick', 'nicholas']
    2 ['nick', 'nicholas']

     

    分析:
    开始name = ["pony","jack"]是全局变量,之后执行change_name()函数吗,在函数中有global关键字,之后针对name做的修改都相当于将name作为全局变量来修改。


    c、注意global的位置

     

    错误例子

     

      

    分析:如果需要global对全局变量进行修改这里的global不能放在name = "nick"下面。

     

     

    5、代码规范:全局变量字母全部大写,局部变量变量名小写。


    二、多层函数的嵌套和作用域


    (1)一定要注意函数要先定义,后使用
    例子1

    def test1():
        print("test1")
    def test2():
        print("test2")
    test1()
    test2()

     


    分析:这样是可以的,先定义函数,再使用函数

    错误例子

    def test1():
        print("test1")
    test2()
    def test2():
        print("test2")
    test1()

     

      


    分析:这样的test2()就无法执行。

    (2)在函数内定义的函数 在外面不能用到


    例子2

    def outer():
        def inner():
            print('inner')
        print('outer')
        inner()
    outer()

     

      


    分析:函数有可见范围,这就是作用域的概念。内部函数不能被外部直接使用。

    例子

    def foo():
        print("foo")
        too()
    def too():
        print("too")
    foo()

     

      


    分析:这里执行顺序是加载def foo():
    加载def too():然后再执行foo(),所以这里不会报错。

    (3)分析多层嵌套函数执行过程及结果
    例子

    NAME = 'nicholas'
    def jack():
        name = "jack"
        print(name)
        def pony():
            name = "pony"
            print(name)
            def charles():
                name = 'charles'
                print(name)
            print(name)
            charles()
        pony()
        print(name)
    jack()

     


    输出结果:

    jack
    pony
    pony
    charles
    jack

     

    分析:

    执行过程如下图

     

    执行顺序:1----2----3----3.1----3.2----3.3----3.4----3.3.1----
    3.3.2----3.3.3----3.3.4----3.3.5--3.3.3.1--3.3.3.2----3.5

     

    1 首先执行NAME = 'nicholas'语句,

    2 加载def jack():函数到内存进行编译,但不执行

    3 调用jack()函数,开始执行

    3.1 执行name = "jack"语句

    3.2 执行print(name)语句,这里由于没有global关键字,优先读取局部变量name = "jack",所以这里输出jack

    3.3 加载def pony():函数到内存进行编译,但不执行

    3.4 调用pony():函数,开始执行

    3.3.1 执行name = "pony"语句,这里是一个局部变量

    3.3.2 执行print(name)语句,这里由于没有global、nonlocal关键字,优先读取局部变量name = "pony",所以这里输出pony

    3.3.3 加载charles():函数到内存进行编译,但不执行

    3.3.4 执行print(name)语句,这里由于没有global、nonlocal关键字,优先读取同一层级的局部变量name = "pony",所以这里输出pony

    3.3.5 调用charles():函数,开始执行

    3.3.3.1 执行name = 'charles'语句,这里是个局部变量

    3.3.3.2 执行print(name)语句,优先读取局部变量name = "charles",所以这里输出charles

    ~~charles():函数结束

    ~~pony():函数


    3.5 执行执行print(name)语句,优先使用同层级的局部变量name = "jack",所以这里输出jack。


    ~~整体结束

     

    例子

     

    name = "nicholas"
    def outer():
        name = "nick"
        def inner():
            print(name)
        print(name)
        inner()
    outer()

     

     

    输出结果

     

    nick
    nick

     

      分析:注意这里的inner()函数内部的print(name)语句,这里仍然是优先使用outer()函数内部的局部变量name = "nick",而非全局变量。

     

    (4)

    nonlocal关键词
    nonlocal,指定上一级变量,如果没有就继续往上直到找到为止
    例子
    看这个程序,分析输出过程和结果。

     

    def scope_test():
        def do_local(): 
            spam = "local spam"  
        def do_nonlocal(): 
            nonlocal spam 
            spam = "nonlocal spam"   
        def do_global(): 
            global spam 
            spam = "global spam"       
        spam = "test spam"
        do_local()
        print("After local assignment:", spam)
        do_nonlocal()
        print("After nonlocal assignment:", spam)
        do_global()  
        print("After global assignment:", spam)       
    scope_test()
    print("In global scope:", spam)

     

      

    输出结果

    After local assignment: test spam
    After nonlocal assignment: nonlocal spam
    After global assignment: nonlocal spam
    In global scope: global spam

     

      

    分析:
    程序执行步骤如图

     

     

    从1开始
    1--2--2.1--2.2--2.3--2.4--2.5--2.5.1--2.5.2--2.6--2.7--2.7.1--2.7.2--2.8

    --2.9--2.9.1--2.9.2--2.10--2.11


    下面具体分析下程序执行的过程

    1 将def scope_test():函数体作为一个整体加载到内存中,但不执行

    2 调用def scope_test():开始执行

    2.1 将def do_local():函数体作为一个整体加载到内存中,但不执行

    2.2 将def do_nonlocal(): 函数体作为一个整体加载到内存中,但不执行

    2.3 将 def do_global(): 函数体作为一个整体加载到内存中,但不执行

    2.4 执行 spam = "test spam"

    2.5 调用 def do_local():函数

    2.5.1 执行 def do_local():函数

    2.5.2 执行 spam = "local spam"

    --完成2.5.2之后 def do_local():函数结束,其所占的内存被回收, spam =

    "local spam"数据被销毁

    2.6 执行print("After local assignment:", spam)语句

    由于没有global关键字,这里优先读取局部变量,即spam = "test spam"

    打印出After local assignment: test spam

    2.7 调用do_nonlocal()函数

    2.7.1 执行def do_nonlocal():

    遇到 nonlocal 声明,nonlocal关键字用来在函数外层(非全局)变量。

    这里的外层即为def scope_test():这个作用域内

    2.7.2 执行spam = "nonlocal spam"语句

    这时def scope_test():这个作用域内由以前的spam = "test spam"被重新覆盖为

    spam = "nonlocal spam"

    --do_nonlocal()函数体结束

    2.8 执行 print("After nonlocal assignment:", spam)语句

    由于spam被重新赋值为"nonlocal spam",这里输出

    After nonlocal assignment: nonlocal spam

    2.9 调用do_global()函数

    2.9.1 执行def do_global(): 函数

    2.9.2 执行 spam = "global spam" 语句

    遇到global声明,global关键字用来在函数整体作用域使用全局变量。类似于在

    def scope_test():上面写了一句spam = "global spam"

    --def do_global(): 函数体结束

    2.10 执行print("After global assignment:", spam)语句

    由于这一层级作用域没有global关键字,这里优先读取局部变量,即被修改过一次的

    spam = "nonlocal spam"

    这里输出After global assignment: nonlocal spam

    2.11执行print("In global scope:", spam)语句

    由于在2.9.2 spam被声明了全局变量,即spam = "global spam"

    所以这里输出

    In global scope: global spam

     

    例子2

    name = "jack"
    def foo():
        name = "nick"
        print(name)
        def too():
            nonlocal name
            name = "nicholas"
            print(1,name)
        too()
        print(name)
    foo()

     

      输出结果

    nick
    1 nicholas
    nicholas

     

      分析:注意这里的def too():函数内print(1,name)语句仍然优先读取局部变量name = "nicholas"。


    三、递归


    1、递归的定义


    如果在调用一个函数的过程中直接或间接调用自身本身,那么这种方法叫做递归。

     

    2、递归的特点


    a、递归必须有一个明确的结束条件(基例)。
    b、每次进入更深一层递归时,问题规模相比上次递归都应有所减少。
    c、递归效率不高,递归层次过多会导致栈溢出。


    3、递归的执行过程

    例子

    def calc(n):
        print(n)
        if int(n/2) ==0:
            return n
        return calc(int(n/2))
    calc(10)

     

      


    输出结果

    10
    5
    2
    1

     


    分析执行过程:

     

    具体过程

    (1)执行def calc(n):语句,将calc(n)函数加载到内存中进行编译,但不执行


    (2)执行calc(10)语句,调用calc(n)函数,将n = 10 传入calc(n)函数


    (3)执行print(n)语句,此时n = 10,打印10


    判断n/2是否等于0,10/2 = 5不等于0


    执行retun语句,return调用calc(n)函数,


    此时具体是执行calc(int(10/2))即calc(5)


    此层函数暂停,等待calc(5)返回值


    (4)执行print(n)语句,此时n = 5,打印5


    判断n/2是否等于0,5/2 = 2不等于0


    执行retun语句,return调用calc(n)函数,


    此时具体是执行calc(int(5/2))即calc(2)


    此层函数暂停,等待calc(2)返回值


    (5)执行print(n)语句,此时n = 2,打印2


    判断n/2是否等于0,2/2 = 1不等于0


    执行retun语句,return调用calc(n)函数,


    此时具体是执行calc(int(2/2))即calc(1)


    此层函数暂停,等待calc(1)返回值


    (6)执行print(n)语句,此时n = 1,打印1


    判断n/2是否等于0,1/2 = 2等于0,


    执行if条件下的retun语句,return n 给上一层函数,


    即return 1给上层函数


    (7)将1传给calc(1),calc(1)得到值为1 ,


    return calc(1)即return 1,再次将1传给上层的return calc(2),


    calc(2)得到值为1,再次将1传给上层的return calc(5),


    calc(5)得到值为1,最后将1传给calc(10),


    即calc(10)= 1。

     

    这里可以打印下calc(10)的值

    def calc(n):
        print(n)
        if int(n / 2) == 0:
            return n
        return calc(int(n / 2))
    v = calc(10)
    print("calc(10)是",v)

     

      输出结果

    10
    5
    2
    1
    calc(10)是 1

     

      

     

    例子2

     

    import time
    person_list=['Pony','Charles','Richard ','Jack']
    print("How can I make good money?")
    def ask(person_list):
        print('-'*60)
        if len(person_list) == 0:
            return "I don't know"
        person=person_list.pop(0)
        if person == "Jack":
            return "%s say:Better have a dream, in case it comes true someday." %person
        print('hi Boss[%s],How can I make good money?' %person)
        print("%s replied:I don't know,But I konw you are a smart boy,hold on ,I can help you ask %s..." %(person,person_list))
        time.sleep(10)
        res=ask(person_list)
        #print('%s say: %res' %(person,res))#注释语句
        return res
     
    res = ask(person_list)
     
    print(res)

     

      


    输出结果

    How can I make good money?
    ------------------------------------------------------------
    hi Boss[Pony],How can I make good money?
    Pony replied:I don't know,But I konw you are a smart boy,hold on ,I can help you ask ['Charles', 'Richard ', 'Jack']...
    ------------------------------------------------------------
    hi Boss[Charles],How can I make good money?
    Charles replied:I don't know,But I konw you are a smart boy,hold on ,I can help you ask ['Richard ', 'Jack']...
    ------------------------------------------------------------
    hi Boss[Richard ],How can I make good money?
    Richard  replied:I don't know,But I konw you are a smart boy,hold on ,I can help you ask ['Jack']...
    ------------------------------------------------------------
    Jack say:Better have a dream, in case it comes true someday.

     

      

     

    如果取消上面print('%s say: %res' %(person,res))注释,执行这一语句,可以看出return返回的过程
    如下

    import time
    person_list=['Pony','Charles','Richard ','Jack']
    print("How can I make good money?")
    def ask(person_list):
        print('-'*60)
        if len(person_list) == 0:
            return "I don't know"
        person=person_list.pop(0)
        if person == "Jack":
            return "%s say:Better have a dream, in case it comes true someday." %person
        print('hi Boss[%s],How can I make good money?' %person)
        print("%s replied:I don't know,But I konw you are a smart boy,hold on ,I can help you ask %s..." %(person,person_list))
        time.sleep(1)
        res=ask(person_list)#第一处
        print('%s say: %res' %(person,res))
        return res
     
    res = ask(person_list)#第二处
     
    print(res)

     


    输出结果

    How can I make good money?
    ------------------------------------------------------------
    hi Boss[Pony],How can I make good money?
    Pony replied:I don't know,But I konw you are a smart boy,hold on ,I can help you ask ['Charles', 'Richard ', 'Jack']...
    ------------------------------------------------------------
    hi Boss[Charles],How can I make good money?
    Charles replied:I don't know,But I konw you are a smart boy,hold on ,I can help you ask ['Richard ', 'Jack']...
    ------------------------------------------------------------
    hi Boss[Richard ],How can I make good money?
    Richard  replied:I don't know,But I konw you are a smart boy,hold on ,I can help you ask ['Jack']...
    ------------------------------------------------------------
    Richard  say: 'Jack say:Better have a dream, in case it comes true someday.'es
    Charles say: 'Jack say:Better have a dream, in case it comes true someday.'es
    Pony say: 'Jack say:Better have a dream, in case it comes true someday.'es
    Jack say:Better have a dream, in case it comes true someday.

     

      

    分析:最后的返回结果是Richard返回给Charles,Charles返回给Pony
    第一处的res=ask(person_list) 就算执行完了,res得到Jack say:Better have a dream, in case it comes true someday.

    然后return给函数外的res,最后打印这句话。

  • 相关阅读:
    【BZOJ2525】[Poi2011]Dynamite 二分+树形DP
    【BZOJ2560】串珠子 状压DP+容斥
    【BZOJ2726】[SDOI2012]任务安排 斜率优化+cdq分治
    [NOIP2017]宝藏 状压DP
    [NOIP2017]逛公园 最短路+拓扑排序+DP
    [NOIP2017]列队 离线+SBT
    【CF628D】Magic Numbers 数位DP
    【BZOJ2791】[Poi2012]Rendezvous 倍增
    sql 通过游标 拆分xml结构
    sql字符转换函数大全
  • 原文地址:https://www.cnblogs.com/insane-Mr-Li/p/9813226.html
Copyright © 2020-2023  润新知