global与nonlocal
一、global
总之一句话,作用域是全局的,就是会修改这个变量对应地址的值。
global语句是一个声明,它适用于整个当前代码块。这意味着列出的标识符将被解释为全局变量。尽管自由变量可以能指的是全局变量而不被声明为全局变量。
global语句中列出的名称不得用于该全局语句之前的文本代码块中。
global语句中列出的名称不能定义为形式参数,也不能在for循环控制目标、class定义、函数定义、impot语句或变量注释中定义。
1、global关键字用来在函数或其他局部作用域中使用全局变量。但是如果不修改全局变量也可以不使用global关键字。
gcount = 0
def global_test():
gcount+=1
print (gcount)
global_test()
以上代码会报错:第一行定义了全局变量,在内部函数中又对外部函数进行了引用并修改,那么python会认为它是一个局部变量,有因为内部函数没有对其gcount进行定义和赋值,所以报错。
2、如果局部要对全局变量修改,则在局部声明该全局变量
gcount = 0
def global_test():
global gcount
gcount+=1
print (gcount)
global_test()
以上输出为:1
3、如果局部不声明全局变量,并且不修改全局变量,则可以正常使用
gcount = 0
def global_test():
print (gcount)
global_test()
二、nonlocal
只在闭包里面生效,作用域就是闭包里面的,外函数和内函数都影响,但是闭包外面不影响。
nonlocal语句使列出的标识符引用除global变量外最近的封闭范围中的以前绑定的变量。这很重要,因为绑定的默认行为是首先搜索本地名称空间。该语句允许封装的代码将变量重新绑定到除全局(模块)作用域之外的本地作用域之外。
nonlocal 语句中列出的名称与 global 语句中列出的名称不同,它们必须引用封闭范围中已经存在的绑定(无法明确确定应在其中创建新绑定的范围)。
1、 nonlocal声明的变量不是局部变量,也不是全局变量,而是外部嵌套函数内的变量
def make_counter():
count = 0
def counter():
nonlocal count
count += 1
return count
return counter
def make_counter_test():
mc = make_counter()
print(mc())
print(mc())
print(mc())
make_counter_test()
以上输出为:
1
2
3
三、混合使用
def scope_test():
def do_local():
spam = "local spam" #此函数定义了另外的一个spam字符串变量,并且生命周期只在此函数内。此处的spam和外层的spam是两个变量,如果写出spam = spam + “local spam” 会报错
def do_nonlocal():
nonlocal spam #使用外层的spam变量
spam = "nonlocal spam"
def do_global():
global spam
spam = "global spam"
spam = "test spam"
do_local()
print("After local assignmane:", spam)
do_nonlocal()
print("After nonlocal assignment:",spam)
do_global()
print("After global assignment:",spam)
scope_test()
print("In global scope:",spam)
以上输出为:
After local assignmane: test spam
After nonlocal assignment: nonlocal spam
After global assignment: nonlocal spam
In global scope: global spam
函数名的多种用法
1.函数可以当作值被赋予变量
def func():
bu = (1,2,3,'abc',{'name':'ligen'})
print (bu)
a = func
a()
2.函数名可以当作函数的参数
def func(*args):
dict1 = {'name':args[0],'age':args[1],'box':args[2]}
for key in dict1:
print (key)
print ('*'*20)
for value in dict1.values():
print (value)
print ('*' * 20)
for kv in dict1.items():
print (kv)
# func('ligen', 24, 'blue')
def func2(f):
f
func2(func('ligen', 24, 'blue'))
3.函数可以当作元素放在容器中
def func1():
print ('1')
def func2():
list1 = [func1,func1,func1]
for i in list1:
i()
func2()
4.函数名可以当作函数的返回值
def func():
def foo():
print(222)
return foo
a = func()
a()
闭包函数
定义在函数内部的函数 并且用到了外部函数名称空间中的名字
1.定义在函数内容
2.用到外部函数名称空间中的名字
给函数传参的方式有俩种,分别为用形参和闭包
1. 形参
def index(a):
print('from index', a)
2. 闭包
def outer(name):
def inner():
print(name)
return inner
x = outer('XWenXiang') # x = outer() = inner
x() # x() = inner()
(1) 代码执行后首先在第一行定义函数 outer() 并定义形参 name
(2) 第二步调用函数 outer() 并传入实参 XWenXiang
(3) 第三步执行函数 outer() 函数体代码,定义函数 inner() ,返回值 inner,inner也就是函数
inner的内存地址
(4) 第四步将函数 outer() 返回值赋值给 x 。
(5) 第五步调用函数 x() 因为此时 x 等价与 inner 的内存地址。
装饰器
1.什么是装饰器
器指的是工具,可以定义成函数
装饰指的是为其他事物添加额外的东西点缀
合到一起的解释:
装饰器指的定义一个函数,该函数是用来为其他函数添加额外的功能
就是拓展原来函数功能的一种函数
2.为何要用装饰器
开放封闭原则
开放:指的是对拓展功能是开放的
封闭:指的是对修改源代码是封闭的
装饰器就是在不修改被装饰器对象源代码以及调用方式的前提下为被装饰对象添加新功能
3.如何用
# 需求:在不修改index函数的源代码以及调用方式的前提下为其添加统计运行时间的功能
def index(x, y):
time.sleep(3)
print('index %s %s' % (x, y))
index(111, 222)
# index(y=111,x=222)
# index(111,y=222)
# 解决方案一:失败
# 问题:没有修改被装饰对象的调用方式,但是修改了其源代码
import time
def index(x, y):
start = time.time()
time.sleep(3)
print('index %s %s' % (x, y))
stop = time.time()
print(stop - start)
index(111, 222)
# 解决方案二:失败
# 问题:没有修改被装饰对象的调用方式,也没有修改了其源代码,并且加上了新功能
# 但是代码冗余
import time
def index(x, y):
time.sleep(3)
print('index %s %s' % (x, y))
start = time.time()
index(111, 222)
stop = time.time()
print(stop - start)
# 解决方案三:失败
# # 问题:解决了方案二代码冗余问题,但带来一个新问题即函数的调用方式改变了
import time
def index(x, y):
time.sleep(3)
print('index %s %s' % (x, y))
def wrapper():
start = time.time()
index(111, 222)
stop = time.time()
print(stop - start)
wrapper()
# 方案三的优化一:将index的参数写活了
import time
def index(x, y, z):
time.sleep(3)
print('index %s %s %s' % (x, y, z))
def wrapper(*args, **kwargs):
start = time.time()
index(*args, **kwargs) # index(3333,z=5555,y=44444)
stop = time.time()
print(stop - start)
wrapper(3333, 4444, 5555)
wrapper(3333, z=5555, y=44444)
# 方案三的优化二:在优化一的基础上把被装饰对象写活了,原来只能装饰index
import time
def index(x,y,z):
time.sleep(3)
print('index %s %s %s' %(x,y,z))
def home(name):
time.sleep(2)
print('welcome %s to home page' %name)
def outter(func):
# func = index的内存地址
def wrapper(*args,**kwargs):
start=time.time()
func(*args,**kwargs) # index的内存地址()
stop=time.time()
print(stop - start)
return wrapper
index=outter(index) # index=wrapper的内存地址
home=outter(home) # home=wrapper的内存地址
home('egon')
# home(name='egon')
# 方案三的优化三:将wrapper做的跟被装饰对象一模一样,以假乱真
import time
def index(x,y,z):
time.sleep(3)
print('index %s %s %s' %(x,y,z))
def home(name):
time.sleep(2)
print('welcome %s to home page' %name)
def outter(func):
def wrapper(*args,**kwargs):
start=time.time()
res=func(*args,**kwargs)
stop=time.time()
print(stop - start)
return res
return wrapper
# # 偷梁换柱:home这个名字指向的wrapper函数的内存地址
home=outter(home)
res=home('egon') # res=wrapper('egon')
print('返回值--》',res)
装饰器模板
def outer(func):
def inner(*args, **kwargs):
res = func(*args, **kwargs)
return res
return inner
装饰器语法糖
在用装饰器之前还要写上 index = calculate_time(index) 这样一行代码,显然是很不方便的。这时候可以用 @calculate_time 替换 index = calculate_time(index)
import time #导入时间模块
def calculate_time(f):
def inner():
start_time = time.time()
f()
end_time = time.time()
print(end_time - start_time)
return inner
@calculate_time # 等价于index = calculate_time(index) 这行赋值的代码
def index():
time.sleep(2.2) #时间休眠2.2秒
index() #2.2051877975463867