python函数作用域
一、 作用域
1、作用域定义
①、全局作用域:在整个程序运行环境中都可见
②、局部作用域:在函数、类等内部可见;局部变量使用范围不能超过其所在的局部作用域
def lovepy():
x = 1 #局部作用域,在lovepy内
def text():
print(x) #错误事例,因为x是lovepy中变量,在其它局部作用域无法对其调用
print(x) #错误事例,因为x是lovepy中变量,在外部无法调用
2、全局变量global
①、使用global将函数内(局部作用域)中某一个变量声明为使用外部的全局作用域定义的变量
②、全局作用域中必须有变量的定义
③、函数的目的就是隔离,因此global尽量不要使用。说明只是为了更好的理解作用域
④、如果函数想使用全局变量,请用函数形参传参解决
#例1
x = 1
def lovepy1():
x = 2
return x #x输出为2为什么
print(lovepy1())
从例1:对于函数内变量的调用原则:先自己内部找然后往上找,不会向下找
#例2
y = 1
def lovepy2():
global y
y = 2
return y # y输出为2为什么,如果把y=2放在global前为什么会报错
print(lovepy2()
print(y) #输出为2
例2:上述的过程是,声明lovepy2中y是全局变量y,然后又把y从新赋值为y,最后其实改变的是全局变量中的y。而把y = 2放在前面,则在全局声明前将y给了2。(后续博客会专门来说python中变量赋值,和其它语言的差别)
#例3
z = 1
def lovepy3():
global z
z += 1
return z # z输出为2为什么,如果不加global,为什么会报错
print(lovepy3())
例3:如果不声明global z 则在z +=1 时会报错,原因是z +=1,是先引用后赋值(如果不加global声明,本地环境又没有这个变量)。对于python而言,动态语言是赋值才算定义,才能被引用(后续会和例2专门统一一节来讲)
3、nonlocal关键字
①、使用nonlocal关键字,将变量标记为在上级作用域中定义,但不能是全局作用域中定义
#例1;错误事例,nonlocal只能在局部作用域中使用
a = 0
def lovepy1():
nonlocal a
a += 1
return a
print(lovepy1())
#例2
def lovepy2():
a = 0
def py():
nonlocal a
a += 1
return a
return py
a = lovepy2()
print(a(),a()) # 输出为 1,2
print(lovepy2()(),lovepy2()()) # 输出为1,1
#思考两个print输出不同的原因,在本章闭包中会说
4、默认值作用域
①、对于python来说,一切皆对象。
②、对于函数来说,可变类型默认值,就可能修改这个默认值
③、有时候这种特性是好的,有时候不好有副作用。如何做到按需改变,一般有两种方法:1、影子copy 2、通过值的判断灵活创建传入对象
例一:理解默认值作用域
#例1:
def lovepy1(a = []):
a.append(1)
return a
print(lovepy1()) #输出[1]
print(lovepy1.__defaults__)
print(lovepy1([2,3,4])) #输出[2,3,4,1]
print(lovepy1.__defaults__)
print(lovepy1()) #输出[1,1]
print(lovepy1.__defaults__)
print(lovepy1([2,3,4])) #输出[2,3,4,1]
print(lovepy1.__defaults__)
#思考如果缺省值是b = 1效果是怎样的?还会出现上面类似的结果吗
对于例1来说:(1)函数lovepy1(),其本身就是个对象,因此默认值放在属性中会伴随着整个函数的整个生命周期。这里要知道lovepy1()整体才是一个函数。而lovepy1只是标识符。(2)、可以使用 (函数标识符.defaults)属性,查看上例活动过程。
例2:影子copy方法,使用影子copy创建一个新的对象,永远不能改变传入的参数
#例2:
def lovepy2(b = []):
b = b[:] #了解 b = b[:] 和b = b.copy()和b = b之间关系
b.append(1)
return b
print(lovepy2()) #输出[1]
print(lovepy2([2,3,4])) #输出[2,3,4,1]
print(lovepy2()) #输出[1]
例3:使用不可变类型的默认值
#例3:
def lovepy3(c = None):
if c is None:
c = []
c.append(1)
return c
print(lovepy3()) #输出[1]
print(lovepy3()) #输出[1]
从例三:(1)、如果使用缺省值None就创建一个列表,如果传入一个列表,就直接修改这个列表。(2)、这种方式更加灵活,很多函数的定义,都可以看到使用None这个不可变的值作为默认参数,可以说这是一种惯用法。
二、闭包
1、相关定义。
①、自由变量:未在本地作用域中定义的变量。例如定义在内存函数外的外层函数作用域的变量
②、闭包:就是一个概念,出现在嵌套函数中,指的是内层函数引用外层函数的自由变量,就行形成了闭包。
2、函数调用函数
#例1:
def lovepy1():
return lovepy2
def lovepy2():
return ("他是大聪明")
print(lovepy1()()) #输出他是大聪明
3、闭包
#例2:
def lovepy2():
b = [0]
def love():
b[0] += 1 #报错吗,为什么
return b[0]
return love
c = lovepy2()
print(c()) #输出1
print(c()) #输出2
b = 10
print(c()) #输出3
print(lovepy2()()) #输出1,为什么
print(lovepy2()()) #输出1,为什么
从例2:(1)、对于闭包无法直接访问外部函数的局部变量,因此大多会使用容器将上层局部变量传递进去。如例中的b = [0],其实我想实现的是b = 0传递下去。(2)、也可以使用nonlocal关键字,传递变量 (3)、当闭包执行完后,仍然能够保持住当前的运行环境。这就是和lovepy2()()和例1的区别。
4、闭包的作用
①、当闭包执行完后,仍然能够保持住当前的运行环境。
②、闭包可以根据外部作用域的局部变量来得到不同的结果。
三、全局函数的销毁
1、重新定义同名函数
def lovepy():
return "他是大聪明"
def lovepy(): #覆盖了上面的函数,其实python也有多态,后续在说
pass
print(lovepy())
2、del 语句删除函数对象
def lovepy():
return "他是大聪明"
def lovepy():
pass
print(lovepy())
del lovepy
print(lovepy())
3、程序结束时
四、局部函数销毁
1、重新在上级作用域定义同名函数
def lovepy():
a = 1
def love():
nonlocal a
a +=1
return a
def love():
nonlocal a
a +=10
return a
return love
foo = lovepy()
print(foo())
2、上级作用域销毁
def lovepy():
a = 1
def love():
nonlocal a
a +=1
return a
return love
foo = lovepy()
print(foo())
del foo
print(foo())
3、del语句删除函数对象
def lovepy():
a = 1
def love():
nonlocal a
a +=1
return a
del love
return love
foo = lovepy()
print(foo())
下一章节讲函数装饰器