60.函数的作用域(全局变量和局部变量)
变量起作用的范围称为变量的作用域,不同作用域内同名变量之间互不影响。
变量分为:全局变量
、局部变量
。
没有必要情况
一般使用局部变量性能高
全局变量:
- 在
函数和类定义之外声明
的变量。作用域为定义的模块
,从定义位置开始直到模块 结束。 - 全局变量
降低了
函数的通用性
和可读性
。应尽量避免全局变量的使用。 - 全局变量一般做
常量使用
。 函数内
要改变全局变量的值
,使用global
声明一下
局部变量:
- 在
函数体中
(包含形式参数)声明的变量。 局部变量
的引用比全局变量快
,优先考虑使用
。- 如果局部变量和全局变量
同名
,则在函数内隐藏全局变量,只使用同名
的局部变量
【操作】全局变量的作用域测试
a = 100 #全局变量
def f1():
global a #如果要在函数内改变全局变量的值,增加 global关键字声明
# 如果不加这一句的话 下面 print(a) 将会报错。
# UnboundLocalError: local variable 'a' referenced before assignment
print(a) #打印全局变量a 的值
a = 300
# 就算放 print(a) 到这里
# 打印的 是 300
# 但是 你在 函数外面 访问 a 的时候 依然 还是 100
f1()
print(a) # 这一句是在 外面 打印 a 但是 却是 300 说明修改成功
执行结果:
100
300
【操作】全局变量和局部变量同名测试
a=100
def f1():
a = 3 # 同名的局部变量
print(a)
f1()
print(a) # a 仍然是100,没有变化
执行结果:
3
100
【操作】 输出局部变量和全局变量
a = 100
def f1(a,b,c):
print("a b c : ",a,b,c)
print("局部 : ",locals()) #打印输出的局部变量
print("全局 : ",globals()) #打印输出的全局变量
f1(2,3,4)
# 运行效果
In [1]: a = 100
...: def f1(a,b,c):
...: print("a b c : ",a,b,c)
...: print("局部 : ",locals()) #打印输出的局部变量
...: print("全局 : ",globals()) #打印输出的全局变量
...:
...: f1(2,3,4)
a b c : 2 3 4
局部 : {'c': 4, 'b': 3, 'a': 2}
全局 : {'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': <module 'builtins' (built-in)>, '__builtins__': <module 'builtins' (built-in)>, '_ih': ['', 'a = 100\ndef f1(a,b,c):\n print("a b c : ",a,b,c) \n print("局部 : ",locals()) #打印输出的局部变量 \n print("全局 : ",globals()) #打印输出的全局变量\n \nf1(2,3,4)'], '_oh': {}, '_dh': ['D:\\'], 'In': ['', 'a = 100\ndef f1(a,b,c):\n print("a b c : ",a,b,c) \n print("局部 : ",locals()) #打印输出的局部变量 \n print("全局 : ",globals()) #打印输出的全局变量\n \nf1(2,3,4)'], 'Out': {}, 'get_ipython': <bound method InteractiveShell.get_ipython of <IPython.terminal.interactiveshell.TerminalInteractiveShell object at 0x04C01510>>, 'exit': <IPython.core.autocall.ExitAutocall object at 0x04C01C50>, 'quit': <IPython.core.autocall.ExitAutocall object at 0x04C01C50>, '_': '', '__': '', '___': '', '_i': '', '_ii': '', '_iii': '', '_i1': 'a = 100\ndef f1(a,b,c):\n print("a b c : ",a,b,c) \n print("局部 : ",locals()) #打印输出的局部变量 \n print("全局 : ",globals()) #打印输出的全局 变量\n \nf1(2,3,4)', 'a': 100, 'f1': <function f1 at 0x042142B8>}
代码说明
a = 3 # 这就是一个全局变量
def test04():
b = 4 # 局部变量
print(b * 10)
# global a # 如果要在函数内改变全局变量的值,增加 global关键字,声明
a = 300
print(a)
test04()
print(a)
# 每次调用函数的时候,python会调用 一个 栈帧 strake frame
# 用完就丢掉了 b在 strake frame 栈帧里面
局部变量和全局变量效率测试
局部变量的查询和访问速度比全局变量快,优先考虑使用
,尤其是在循环的时候。 在特别强调效率的地方或者循环次数较多的地方,可以通过将全局变量转为局部变量提高运 行速度。
代码如下:
import math
from timeit import timeit
# 代码段 1 全局变量
test_global_code="""
def test05():
for k in range(10000000):
math.sqrt(30)
"""
# 代码段 2 局部变量 math 赋给 b
test_local_code="""
def test06():
b=math.sqrt
for k in range(10000000):
b(3)
"""
# 用 timeit 来调用 number 是执行次数
# 乘以 1000 是因为 换算为 毫秒 ms 数值看起来大一点。
timeit(test_global_code,number=10)*1000 # 全局 timeit 返回的是一个 浮点数
timeit(test_local_code,number=10)*1000 # 局部
In [4]: test_global_code="""
...: def test05():
...: for k in range(10000000):
...: math.sqrt(30)
...: """
In [5]: test_local_code="""
...: def test06():
...: b=math.sqrt
...: for k in range(10000000):
...: b(3)
...: """
In [23]: timeit(test_local_code,number=1000000)*1000 # 局部
Out[23]: 66.5988000000084
In [24]: timeit(test_global_code,number=1000000)*1000 # 全局。
Out[24]: 67.5967999999898
# 大家不要看 这里 只是提升了 一毫秒
# 因为 math 导入进来之后本来就挺快的
# 因为如果是你自己写的 变量。 那速度提升就很可观了。
关于一个生成默认参数的函数
def test():
for i in range(3):
def a():
f=i
# 这里的i 本来是无法保存的
return f
arr.append(partial(a,i)) # 用这个偏函数保存到 默认参数中去了。
# 因为函数 a 没有 调用 所以 无法开辟空间 也就无法 维护 每一个 i 的值。记录i 的值。
看一个js
的例子
js
es6 是有 let
这样的声明关键字的。 然后他声明的 是块级作用域 。
下面例子 i 只能在 {}
中生效,每循环一次 就会 开辟一块 空间 。 这个内存空间 就是作用域。
和我们python
中 函数的局部作用 差不多。 每次调用函数 都有临时 开辟一块 空间。 之后再 垃圾回收。
let arr=[]
for(let i=0;i<3;i++){
arr.push(function(){
return i
})
}