作用域
标识符的可见范围就是作用域,也就是常说的变量的作用域
全局作用域
在整个代码运行环境内都可见
举例
x = 5def fn():
print(x)
fn()
运行结果
5
局部作用域
在函数、类内部可见。局部变量的范围不能超过其所在的局部作用域
def fn():
x= 5
returnx
print(fn())
运行结果
5
错误的使用作用域(函数内部变量不能被外部引用)
def fn():
x= 5
returnx
print(fn())
def fn1():
print(x) ## 这里上面fn函数中的x对fn1函数不可见
print(fn1())
print(x) ## x对全局不可见
嵌套函数中的作用域
def outer():
x= 10def inner():
print("inner {}".format(x))
print("outer {}".format(x))
inner()
outer()
运行结果
outer 10inner10
与上面函数中x变量的对比
def outer():
x= 10def inner():
x= 5print("inner {}".format(x))
print("outer {}".format(x))
inner()
outer()
运行结果
outer 10inner5
换一个执行顺序理解
def outer():
x= 10def inner():
x= 5print("inner {}".format(x))
inner()
print("outer {}".format(x))
outer()
运行结果
inner 5outer10
从结果可以看出:
(1)外层变量 x 的作用域在内层函数中可见
(2)内层函数中定义了变量 x,是重新定义的变量x,没有覆盖外层函数中的x。也就是说内层函数有本地定义的变量,优先用自己的
分析如下代码:
代码1
x = 1def fn():
y= x + 1print(y)
print(x)
fn()
运行结果
2
1
代码2
x = 1def fn():
y= x + 1print(y)
x+= 1## 这句开始报错
print(x) fn()
运行结果
Traceback (most recent call last):
y = x + 1UnboundLocalError: local variable 'x' referenced before assignment
报错是因为:x+=1等价x = x + 1,在函数本地定义了一个x变量,等式需要先计算右侧,这时本地没有变量x,x还没有赋值就被引用。
要解决上面的问题,就需要引入如下两个定义:本地变量和全局变量
全局变量 global
使用global关键字定义变量,将函数内的变量声明为全局作用域中的变量。global关键字后面的变量名必须在全局作用域中定义过
x = 1def fn():globalx
x+= 1print(x)
fn()
运行结果
2
全局作用域中没有定义x的情况
x = 10
def fn():globalx
x= 1x+= 1print(x)
fn()
print(x) ## 由于提升了作用域,外部定义的x值被覆盖了
运行结果
2
2
注意,这里的x的作用域还是全局的
不要使用global全局变量,因为这样破坏了函数的隔离性
global关键字定义变量的作用域
def outer():
x= 10def inner():globalx
x+= 1
returnxreturninner
foo=outer()
foo()
运行结果
Traceback (most recent call last):
x+= 1NameError: name'x' is not defined
global的作用域是当前定义global代码段和函数最外层(outer函数外)
默认值的作用域
def fn(abc=1):
print(abc)
fn()
print(abc) ## abc是形参,在函数的局部作用域中
运行结果
1Traceback (most recent call last):
File"C:/Users/ASUS-PC/PycharmProjects/复习/生成器.py", line 117, in 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 1python xyz
(1,) 1279837100776hello1hello 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 1python xyz
None1995116403432hello1hello 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 isNone:
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