一 名称空间
1.1 内建名称空间:内置函数名,1个
1.2 全局名称空间:函数外部的变量 函数名 类名 导入的模块名。1个
1.3 局部名称空间:函数内变量,多个
`名称空间的加载顺序是:内置名称空间->全局名称空间->局部名称空间`
`查找顺序为:局部名称空间->全局名称空间->内置名称空间
二 作用域
全局作用域与局部作用域
作用域与名字查找的优先级
三 嵌套函数
1、嵌套函数简单的理解可以看作是在函数的内部再定义函数,实现函数的“私有”。
2、特点:
<1> 函数内部可以再次定义函数。
<2> `只有被调用时才会执行(外部函数被调用后,被嵌套函数调用才有效)`
一 名称空间
名称空间即存放名字与对象映射/绑定关系的地方。对于x=3,Python会申请内存空间存放对象3,然后将名字x与3的绑定关系存放于名称空间中,del x表示清除该绑定关系。
在程序执行期间最多会存在三种名称空间
1.1 内建名称空间:内置函数名,1个
伴随python解释器的启动/关闭而产生/回收,因而是第一个被加载的名称空间,用来存放一些内置的名字
,比如内建函数名
>>> max
<built-in function max>
>>> sorted
<built-in function sorted>
存活周期:python解释器启动--产生, 解释器关闭--灭亡
1.2 全局名称空间:函数外部的变量 函数名 类名 导入的模块名。1个
伴随python文件的开始执行/执行完毕而产生/回收,是第二个被加载的名称空间,文件执行过程中产生的名字都会存放于该名称空间中,如下名字
存活周期:python文件执行--产生, python文件运行完毕后--灭亡
import sys #模块名sys
x=1 #变量名x
if x == 1:
y=2 #变量名y
def foo(x): #函数名foo
y=1
def bar():
pass
Class Bar: #类名Bar
pass
1.3 局部名称空间:函数内变量,多个
伴随函数的调用/结束而临时产生/回收,函数的形参、函数内定义的名字都会被存放于该名称空间中
局部变量在函数调用时才能够被创建,在函数调用之后会自动销毁
存活周期:调用函数--产生, 调用完毕--灭亡
def foo(x):
y=3 #调用函数时,才会执行函数代码,名字x和y都存放于该函数的局部名称空间中
1.4 加载顺序和查找顺序
名称空间的加载顺序是:内置名称空间->全局名称空间->局部名称空间
查找顺序为:局部名称空间->全局名称空间->内置名称空间。``L-E-G-B E:闭包作用域
画图理解:
二 作用域
2.1 全局作用域与局部作用域
按照名字作用范围的不同可以将三个名称空间划分为两个区域:
- 全局作用域:位于全局名称空间、内建名称空间中的名字属于全局范围,该范围内的名字全局存活(除非被删除,否则在整个文件执行过程中存活)、全局有效(在任意位置都可以使用);
- 局部作用域:位于局部名称空间中的名字属于局部范围。该范围内的名字临时存活(即在函数调用时临时生成,函数调用结束后就释放)、局部有效(只能在函数内使用)。
2.2 作用域与名字查找的优先级
在局部作用域查找名字时,起始位置是局部作用域,所以先查找局部名称空间,没有找到,再去全局作用域查找:先查找全局名称空间,没有找到,再查找内置名称空间,最后都没有找到就会抛出异常
x=100 #全局作用域的名字x
def foo():
x=300 #局部作用域的名字x
print(x) #在局部找x
print(globals())
print(locals())
foo()#结果为300
print(globals())
print(locals())
提示:可以调用内建函数locals()和globals()来分别查看局部作用域和全局作用域的名字,查看的结果都是字典格式。在全局作用域查看到的locals()的结果等于globals()
Python支持函数的嵌套定义,在内嵌的函数内查找名字时,会优先查找自己局部作用域的名字,然后由内而外一层层查找外部嵌套函数定义的作用域,没有找到,则查找全局作用域
x=1
def outer():
x=2
def inner(): # 函数名inner属于outer这一层作用域的名字
x=3
print('inner x:%s' %x)
inner()
print('outer x:%s' %x)
outer()
#结果为
inner x:3
outer x:2
在函数内,无论嵌套多少层,都可以查看到全局作用域的名字,若要在函数内修改全局名称空间中名字的值,当值为不可变类型时,则需要用到global关键字
x=1
def foo():
global x #声明x为全局名称空间的名字
x=2
foo()
print(x) #结果为2
当实参的值为可变类型时,函数体内对该值的修改将直接反应到原值,
num_list=[1,2,3]
def foo(nums):
nums.append(5)
foo(num_list)
print(num_list)
#结果为
[1, 2, 3, 5]
对于嵌套多层的函数,使用nonlocal关键字可以将名字声明为来自外部嵌套函数定义的作用域(非全局)
def f1():
x=2
def f2():
nonlocal x
x=3
f2() #调用f2(),修改f1作用域中名字x的值
print(x) #在f1作用域查看x
f1()
#结果
3
nonlocal x会从当前函数的外层函数开始一层层去查找名字x,若是一直到最外层函数都找不到,则会抛出异常。
三 嵌套函数
1、嵌套函数简单的理解可以看作是在函数的内部再定义函数,实现函数的“私有”。
2、特点:
<1> 函数内部可以再次定义函数。
<2> 只有被调用时才会执行(外部函数被调用后,被嵌套函数调用才有效)
def func1():
print('hello world!')
def func2():
print('everyone...')
func1() # 输出:hello world! ,此时func2未被调用并不执行
age = 21
def func1():
age = 19
print('func1输出:',age) # 输出:19
def func2():
print('func2输出:',age) # 输出:19
return func2 # 返回函数名
val = func1() # 调用func1并将返回值赋给变量val,val = func2
print(val) # 输出:<function func1.<locals>.func2 at 0x000002CE667298C8>
val() # 等于调用func2,返回函数定义处执行
print('全局输出:',age) # 输出:21
1 #!/usr/bin/env python3
2 #-*- coding:utf-8 -*-
3
4 #情形1
5 name = 'cc'
6 def func1():
7 name = 'sc'
8 print(name)
9 def func2():
10 #name = 'df'
11 print(name)
12 func2()
13 func1() #输出:sc df,当第10行被注释时,输出:sc sc,
14 # 嵌套函数中的变量层层调用,当前函数中没有便向上一级函数查找
15
16 #情形2
17 age = 21
18 def func1():
19 def func2():
20 print(age) #输出:18,当前函数中并没有定义(‘第二层局部变量’)age,向第一层找
21 age = 18 #与func2位于同一层,‘第一层局部变量’
22 func2()
23 print(func1(),age) #输出:None ,21,此时,函数外输出的age只能是全局变量
24
25 #情形3
26 age = 21
27 def func1():
28 def func2():
29 print(age)
30 func2()
31 #age = 18
32 func1() #当31行不被注释时,运行会报错,打印age时会混淆全局age和局部age;
33 #当31行被注释时,输出: 21,此时func2调用全局age
34
35 #情形4
36 age = 21
37 def func1():
38 global age #声明全局变量
39 def func2():
40 print('func2中的:',age) #输出>>func2中的: 21, age此时引用的是36行的全局变量
41 func2()
42 age = 18 #由于已经声明age是全局变量,所以此处的重新赋值就是对全局修改
43 func1()
44 print('全局中的:',age) #输出>>全局中的: 18, 变量age在函数func1中被修改
45
46 #情形5
47 age = 21
48 def func1():
49 global age
50 def func2():
51 print('func2中的:',age) #输出>>func2中的: 18, age此时引用的是47行的全局变量
52 age = 18 #由于已经声明age是全局变量,所以此处的重新赋值就是对全局修改
53 func2()
54 func1()
55 print('全局中的:',age) #输出>>全局中的: 18, 变量age在函数func1中被修改
''' 情形4和情形5的区别在于age变量的修改在程序中的执行顺序,在func2调用前还是在func2调用之后,如果是在func2调用前修改age,则func2中输出的则是修改后的;如果是在func2调用后修改age,则func2中输出的就是未修改时的全局变量age,所以两者func2函数中输出的age值不同,但最后的全局输出相同。'''