一、局部变量与全局变量

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']

  

分析:这里无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的位置

错误例子

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

  

分析:如果需要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,最后打印这句话。

Python之路(第六篇)Python全局变量与局部变量、函数多层嵌套、函数递归的更多相关文章

  1. Python之路(第八篇)Python内置函数、zip()、max()、min()

    一.python内置函数 abs() 求绝对值 例子 print(abs(-2)) all() 把序列中每一个元素做布尔运算,如果全部都是true,就返回true, 但是如果是空字符串.空列表也返回t ...

  2. 【Python之路】特别篇--Python装饰器

    前情提要 1. 作用域 在python中,函数会创建一个新的作用域.python开发者可能会说函数有自己的命名空间,差不多一个意思.这意味着在函数内部碰到一个变量的时候函数会优先在自己的命名空间里面去 ...

  3. Python之路(第七篇)Python作用域、匿名函数、函数式编程、map函数、filter函数、reduce函数

    一.作用域 return 可以返回任意值例子 def test1(): print("test1") def test(): print("test") ret ...

  4. 【Python之路】特别篇--Python文件操作

    文件操作 open函数,该函数用于文件处理 操作文件时,一般需要经历如下步骤: (1)打开文件 (2)操作文件 一.打开文件 文件句柄 = open('文件路径', '模式','编码') 打开文件时, ...

  5. 【Python之路】第九篇--Python基础之线程、进程和协程

    进程与线程之间的关系 线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除.线程可与属于同一进程的其它线程共享进程所拥有的全 ...

  6. Python之路(第五篇) Python基本数据类型集合、格式化、函数

    一.变量总结 1.1 变量定义 记录某种状态或者数值,并用某个名称代表这个数值或状态. 1.2 变量在内存中的表现形式 Python 中一切皆为对象,数字是对象,列表是对象,函数也是对象,任何东西都是 ...

  7. 【Python之路】特别篇--Python内置函数

    abs() 求绝对值 i = abs(-100) print(i) # 100 all() 循环里面的参数 如果每个元素都为真,那么all返回值为真  假: 0 False None "&q ...

  8. 【Python之路】特别篇--Python面向对象(进阶篇)

    上一篇<Python 面向对象(初级篇)>文章介绍了面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使 ...

  9. 【Python之路】特别篇--Python切片

    字符串切片操作 切片操作符是序列名后跟一个方括号,方括号中有一对可选的数字,并用冒号分割. 注意: 数是可选的,而冒号是必须的. consequence[start:end:step] 切片操作符中的 ...

随机推荐

  1. SQL Select语句完整的执行顺序(转)

    SQL Select语句完整的执行顺序: 1.from子句组装来自不同数据源的数据: 2.where子句基于指定的条件对记录行进行筛选: 3.group by子句将数据划分为多个分组: 4.使用聚集函 ...

  2. dns隧道 dns2tcpd

    有些网络的防火墙设置只允许端口53的UDP流量,就不能通过tcp搭建隧道,这种情况下我们可以通过UDP搭建DNS隧道,通过搭建一个DNS服务器委派的子域,这个子域因为使我们自己搭建的主机,这时候就可以 ...

  3. day14 迭代器和生成器

    1.迭代器 名词解释 什么是迭代:迭代是一个重复过程,但是每次重复都是基于上一次的结果而继续的 #下列循环只是单纯的重复,没有意义 while True: print(1) #基于索引的迭代取值 l ...

  4. xshell 5 书写汉字乱码

    使用win10安装xshell5后发现使用xshell连接虚拟机后,能识别中文但是无法输入中文了   控制面板--语言   删除除中文输入法之外的所有输入法和美式键盘 然后就可以正常输入中文了,为了英 ...

  5. 打印信息,通过.jasper工具将集合输出到PDF文件 然后利用打印机打印文件

    我们上一次成功的利用iReport工具制作了一张报表,并且预览了报表最后的效果,也生成了格式为“jrpxml”.“jrxml”与“jasper”的文件.这次,我们使用jasper提供的java的api ...

  6. Balanced Numbers (数位DP)

    Balanced Numbers https://vjudge.net/contest/287810#problem/K Balanced numbers have been used by math ...

  7. 浅谈CSRF

    CSRF是什么? (Cross Site Request Forgery, 跨站域请求伪造)是一种网络的攻击方式,它在 2007 年曾被列为互联网 20 大安全隐患之一,也被称为“One Click ...

  8. HDU 2680 Choose the best route(SPFA)

    Problem DescriptionOne day , Kiki wants to visit one of her friends. As she is liable to carsickness ...

  9. f5健康检查

    1.1)一般pool的健康检查 Pool member 2)检查member的多个端口,若有任意一个端口down,则切换到另一member Pool的健康检查不填,pool member的健康检查填多 ...

  10. ClickHouse高性能数据库

    ClickHouse之简单性能测试   前面的文章ClickHouse之初步认识已经简单的介绍了ClickHouse,接下来进行简单的性能测试.测试数据来源于美国民用航班的数据,从1987年到2017 ...