作用域
标识符的可见范围就是作用域,也就是常说的变量的作用域
全局作用域
在整个代码运行环境内都可见
举例
x = 5 def fn(): print(x) fn()
运行结果
5
局部作用域
在函数、类内部可见。局部变量的范围不能超过其所在的局部作用域
def fn(): x = 5 return x print(fn())
运行结果
5
错误的使用作用域(函数内部变量不能被外部引用)
def fn(): x = 5 return x print(fn()) def fn1(): print(x) ## 这里上面fn函数中的x对fn1函数不可见 print(fn1()) print(x) ## x对全局不可见
嵌套函数中的作用域
def outer(): x = 10 def inner(): print("inner {}".format(x)) print("outer {}".format(x)) inner() outer()
运行结果
outer 10 inner 10
与上面函数中x变量的对比
def outer(): x = 10 def inner(): x = 5 print("inner {}".format(x)) print("outer {}".format(x)) inner() outer()
运行结果
outer 10 inner 5
换一个执行顺序理解
def outer(): x = 10 def inner(): x = 5 print("inner {}".format(x)) inner() print("outer {}".format(x)) outer()
运行结果
inner 5 outer 10
从结果可以看出:
(1)外层变量 x 的作用域在内层函数中可见
(2)内层函数中定义了变量 x,是重新定义的变量x,没有覆盖外层函数中的x。也就是说内层函数有本地定义的变量,优先用自己的
分析如下代码:
代码1
x = 1 def fn(): y = x + 1 print(y) print(x) fn()
运行结果
2 1
代码2
x = 1 def fn(): y = x + 1 print(y) x += 1 ## 这句开始报错
print(x) fn()
运行结果
Traceback (most recent call last):
y = x + 1 UnboundLocalError: local variable 'x' referenced before assignment
报错是因为:x+=1等价x = x + 1,在函数本地定义了一个x变量,等式需要先计算右侧,这时本地没有变量x,x还没有赋值就被引用。
要解决上面的问题,就需要引入如下两个定义:本地变量和全局变量
全局变量 global
使用global关键字定义变量,将函数内的变量声明为全局作用域中的变量。global关键字后面的变量名必须在全局作用域中定义过
x = 1 def fn(): global x x += 1 print(x) fn()
运行结果
2
全局作用域中没有定义x的情况
x = 10
def fn(): global x x = 1 x += 1 print(x) fn() print(x) ## 由于提升了作用域,外部定义的x值被覆盖了
运行结果
2 2
注意,这里的x的作用域还是全局的
不要使用global全局变量,因为这样破坏了函数的隔离性
global关键字定义变量的作用域
def outer(): x = 10 def inner(): global x x += 1 return x return inner foo=outer() foo()
运行结果
Traceback (most recent call last): x += 1 NameError: name 'x' is not defined
global的作用域是当前定义global代码段和函数最外层(outer函数外)
默认值的作用域
def fn(abc=1): print(abc) fn() print(abc) ## abc是形参,在函数的局部作用域中
运行结果
1 Traceback (most recent call last): File "C:/Users/ASUS-PC/PycharmProjects/复习/生成器.py", line 117, in <module> print(abc) NameError: name 'abc' is not defined
函数的属性
Python使用元组报错函数默认值的属性
引用类型
def fn(abc=[]): abc.append(1) print(abc) fn() fn()
运行结果
[1] [1, 1]
默认值可以在函数的属性中查看,函数的属性伴随着整个函数的生命周期
def fn(abc=[],a=1): abc.append(1) print(abc) fn() print(fn.__defaults__,id(fn)) fn() print(fn.__defaults__,id(fn))
运行结果
[1] ([1], 1) 2304408771304 [1, 1] ([1, 1], 1) 2304408771304
非引用类型
def fn(abc, a=1): print(abc, a) a = 'xyz' print(abc, a) fn('python') print(fn.__defaults__, id(fn)) fn('hello') print(fn.__defaults__, id(fn))
运行结果
python 1 python xyz (1,) 1279837100776 hello 1 hello xyz (1,) 1279837100776
关键字参数的默认值属性
def fn(abc,*, a=1,b=2): print(abc, a) a = 'xyz' print(abc, a) fn('python') print(fn.__defaults__, id(fn)) fn('hello') print(fn.__kwdefaults__, id(fn))
运行结果
python 1 python xyz None 1995116403432 hello 1 hello xyz {'a': 1, 'b': 2} 1995116403432
使用浅拷贝的方式,避免修改实参的值
def fn(abc=[], *, a=1, b=2): abc = abc[::-1] abc.append(1) print(abc) fn() print(fn.__defaults__, id(fn)) fn() print(fn.__kwdefaults__, id(fn)) fn([1, 2]) print(fn.__defaults__, id(fn)) fn([1, 2, 5]) print(fn.__defaults__, id(fn))
运行结果
[1] ([],) 1883111578344 [1] {'a': 1, 'b': 2} 1883111578344 [2, 1, 1] ([],) 1883111578344 [5, 2, 1, 1] ([],) 1883111578344
通过判断传入参数的值,决定修改传入对象的值还是创建新对象(非常常用)
def fn(abc=None, *, a=1, b=2): if abc is None: abc=[] abc.append(1) print(abc) fn() print(fn.__defaults__, id(fn)) fn([1, 2]) print(fn.__defaults__, id(fn)) fn([1, 2, 5]) print(fn.__defaults__, id(fn))
运行结果
[1] (None,) 2462419475176 [1] {'a': 1, 'b': 2} 2462419475176 [1, 2, 1] (None,) 2462419475176 [1, 2, 5, 1] (None,) 2462419475176