Python 函数的作用域

作用域

标识符的可见范围就是作用域,也就是常说的变量的作用域

 全局作用域

在整个代码运行环境内都可见

举例

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
原文地址:https://www.cnblogs.com/zh-dream/p/13836573.html