zoukankan      html  css  js  c++  java
  • 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,最后打印这句话。

  • 相关阅读:
    poj 1328 Radar Installation (贪心)
    hdu 2037 今年暑假不AC (贪心)
    poj 2965 The Pilots Brothers' refrigerator (dfs)
    poj 1753 Flip Game (dfs)
    hdu 2838 Cow Sorting (树状数组)
    hdu 1058 Humble Numbers (DP)
    hdu 1069 Monkey and Banana (DP)
    hdu 1087 Super Jumping! Jumping! Jumping! (DP)
    必须知道的.NET FrameWork
    使用记事本+CSC编译程序
  • 原文地址:https://www.cnblogs.com/insane-Mr-Li/p/9813226.html
Copyright © 2011-2022 走看看