变量作用域
Date: 2019-05-27
Author: Sun
变量由作用范围来限制,按照作用域分类可分为:
- 全局(global): 在函数外部定义
- 局部(local):在函数内部定义
1. 命名空间
比如有一个学校,有5个班级,在2班和4班中都有一个叫“小明”的同学,如果在学校的广播中呼叫“小明”时,2班和4班中的这2个人就纳闷了,你是喊谁呢!!!如果是“2班的小王”的话,那么就很明确了,那么此时的2班就是小王所在的范围,即命名空间
Python中有三种命名空间
a) 局部,函数内的命名空间就是局部的;
b) 全局,模块内的命名空间就是全局的;
c) 内置,包括异常类型、内建函数和特殊方法,可以代码中任意地方调用;
命名空间特点
Python会把命名后的变量和函数分配到不同的命名空间,并通过名称来识别它们。
Python命名空间是名称到对象的映射,这就像是字典,键名是变量名,值是变量的值。
在Python程序执行过程中,会有局部命名空间、全局命名空间和内建命名空间同时存在。
局部命名空间记录函数内部的变量、传入函数的参数、嵌套函数等被命名的对象;
全局命名空间记录模块的变量、函数、类及其它导入的模块等被命名的对象;
locals() 和globals()
在python中,变量是以字典形式存储的存储的
globals()是全局变量; locals()则是局部变量
# -*- coding: utf-8 -*-
__author__ = 'sun'
__date__ = '2019/5/27 18:35'
k = 100
def print_1():
i = 1
j = 2
print('locals():', locals())
print('globals():', globals())
print_1()
输出:
locals(): {'j': 2, 'i': 1}
globals(): {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000028171B0EFD0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'F:/Tx_Project/basic_project/day02/demo/test3.py', '__cached__': None, '__author__': 'sunnychou', '__date__': '2019/5/18 18:35', 'k': 100, 'print_1': <function print_1 at 0x0000028171A12EA0>}
案例
内建命名空间记录Python自身提供的函数、模块等被命名的对象。
>>> x = 3
>>> globals()
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', '__doc__': None, 'x': 3, '__package__': None}
可以看到变量x,3以字典的形式存放在globals空间内。以之对应的名字空间还有:locals()。
>>> locals()
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', '__doc__': None, 'x': 3, '__package__': None}
实际上,你可以通过向名字添加键名和值:
>>> globals()['y'] = 5
>>> y
5
全局变量与局部变量
全局变量是在整个py文件中声明,全局范围内都可以访问
局部变量是在某个函数中声明的,只能在该函数中调用它,如果试图在超出范围的地方调用, 就会出问题
全局变量与局部变量两者的本质区别就是在于作用域
2.变量作用域
作用域是针对变量而言,指申明的变量在程序里的可应用范围。或者称为变量的可见性。
按照作用域划分,可以分为:
-
L:Local,局部作用域
-
E:Enclosing,闭包作用域【闭包外的函数中定义的变量】
-
G:Global,全局作用域 在所有函数外定义的变量
-
B:Built-in,內建作用域【内置作用域】
说明
一般在函数体外定义的变量成为全局变量,在函数内部定义的变量称为局部变量
全局变量所有作用域都可读,局部变量只能在本函数可读
函数在读取变量时,优先读取函数本身自有的局部变量,再去读全局变量
全局变量在局部可以使用(即函数内部可以方位函数外部定义的变量)
案例分析
#1 局部作用域
#局部变量只能在函数内部使用,外部无法引用
#局部变量的作用域从定义开始到函数体结束
def demo():
num = 20 #局部变量
print(num)
demo()
#print(num) 错误
#函数作用域
def outter():
x = 10 #函数作用域,从定义开始到本函数结束
def inner():
y = x #在闭包中可以引用
print(y)
return inner
pf = outter()
pf() #执行闭包
print(pf.__closure__)
#全局作用域
x = 100 #全局作用域 从定义开始到本文件结束
def demo():
print(x)
print(x)
#内建作用域,是指系统内建的函数或常量,在系统载入时加载,在所有模块中都可以直接引用
#比如说系统函数
print(max(1,2,3)) #max函数就是内建作用域 哪里都可以引用
def demo():
x = 30
y = 50
print(max(x, y))
变量作用域查找规则
以 L --> E --> G -->B 的规则查找,即:在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,最后到内建作用域中找。
python作用域种类
问题:想想此时运行下面的程序会有输出吗?执行会成功吗?
# -*- coding: utf-8 -*-
__author__ = 'sun'
__date__ = '2019/5/27 13:52'
for i in range(5):
name = "andy"
print(name)
for i in range(10):
age = i
print(age)
输出:
andy
9
代码执行成功,没有问题;在Java/C#中,执行上面的代码会提示name,age没有定义,而在Python中可以执行成功,这是因为在Python中是没有块级作用域的,代码块里的变量,外部可以调用,所以可运行成功
(2)局部作用域
Python中没有块级作用域,但是有局部作用域
# -*- coding: utf-8 -*-
__author__ = 'sun'
__date__ = '2019/5/27 13:52'
# 局部作用域
def func():
name = "sunny"
print(name)
name变量只在func()函数内部中生效,所以在全局中是没法调用的
问题:
如果在print(name)前调用func()函数呢?对结果是否有影响?
(3)闭包作用域
闭包的作用域,是一个或多个命名空间按照一定规则叠加影响代码区域;运行时动态的作用域,是按照特定层次组合起来的命名空间。
# -*- coding: utf-8 -*-
__author__ = 'sun'
__date__ = '2019/5/27 13:52'
i = 100
def g():
print(i)
def f():
i = 200
g()
f()
上述运行结果是100
(4)全局作用域
先执行如下程序,观察结果
# -*- coding: utf-8 -*-
__author__ = 'sun'
__date__ = '2019/5/27 13:52'
number = 100
def process():
i = 5 #局部变量,局部作用域
number += 1
print(f"i is {i}, number is {number}")
process()
此时会出现UnboundLocalError: local variable 'number' referenced before assignment错误。
问题:如何让一个函数内能访问一个全局变量呢?
# -*- coding: utf-8 -*-
__author__ = 'sun'
__date__ = '2019/5/27 13:52'
number = 100
def process():
i = 5 #局部变量,局部作用域
global number
number += 1
print(f"i is {i}, number is {number}")
process()
通过以上例子,可以知道,通过global关键字。