函数作用域

作用域

一个标识符的可见范围,这就是标识符的作用域。一般常说的是变量的作用域。

def foo():
    x=100


print(x)# 可以访问到吗?

上例中的 x 不可以访问到,会抛出异常 NameError: name 'x' is not defined),原因在于函数是一个封装,它会开辟一个作用域,x变量被限制在这个作用域,所以函数外部x变量不可见。

注意: 没一个函数都会开辟一个作用域

作用域分类

全局作用域

        在整个程序运行环境中都可见

        全局作用域中的变量称为全局变量

局部作用域

        在函数、类 等内部可见

        局部作用域中的变量称为局部变量,其使用范围不能超过其所在局部作用域

        也称为本地作用域local

# 局部变量
def fn1():
    x = 1 # 局部作用域,x为局部变量,使用范围在fn1内
def fn2():
    print(x) # x能打印吗?可见吗?为什么?
print(x) # x能打印吗?可见吗?为什么?
# 全局变量
x = 5 # 全局变量,也在函数外定义
def foo():
    print(x) # 可见吗?为什么?
foo()

 函数嵌套

在一个函数中定义了另外一个函数

def outer():
    def inner():
        print("inner")
    inner()
    print("outer")
outer() # 可以吗?
inner() # 可以吗?

内部函数 inner 不能在外部直接使用,会抛出NameError异常,因为它在函数外部不可见

其实,inner 不过就是一个标识符,就是一个函数outer内部定义的变量而已。

嵌套解构的作用域

def outer1():
    o = 65
    def inner():
        print('inner', o, chr(o))
    inner()
    print('outer', o, chr(o))
outer1() # 执行后,打印什么
def outer2():
    o = 65
    def inner():
        o = 97
        print('inner', o, chr(o))
    inner()
    print('outer', o, chr(o))
outer2() # 执行后,打印什么

从执行的结果来看:

        外层变量在内部作用域可见

       内层作用域inner中,如果定义了 o = 97 ,相当于在当前函数inner作用域中重新定义了一个新的 变量o,但是,这个o并不能覆盖掉外部作用域outer2中的变量o。只不过对于inner函数来说,其 只能可见自己作用域中定义的变量o

一个赋值语句的问题

 报错:

原因分析:

  •        x+=1 其实是x=x+1
  • 只要 有 “x=” 出现,这就是赋值语句。相当于在foo内部定义一个局部变量x,那么foo内部所有x都输这个局部变量x了
  • x=x+1 相当于使用了局部变量x,但是这个x还没有完成赋值,就被右边拿来做加1操作了

x = 5
def foo():   # 函数被解释器解释,foo指向函数对象,同时解释器会理解x是什么作用域
    print(x) # x 在函数解析时就被解释器判定为局部变量
    x += 1   # x = x + 1
foo() # 调用时

如何解决这个常见的问题?

golbal 语句

x=5
def foo():
    gloabal x
    x+=1
    print(x)
foo()

使用global关键字的变量,将foo内部x声明为使用外部的全局作用域中的定义x

全局作用域中必须有x的定义

总结

  • x+=1 这种是特殊形式产生的错误的原因?先引用后赋值,而python 动态语言是赋值才算定义,才能被引用。解决办法,在这条语句前增加x=0之类的赋值语句,或者使用global 告诉内部作用域,去全局作用域查找变量定义
  • 内部作用域使用 x = 10 之类的赋值语句会重新定义局部作用域使用的变量 x ,但是,一旦这个作用
    域中使用 global 声明 x 为全局的,那么 x=5 相当于在为全局作用域的变量 x 赋值

 global 使用原则

  • 外部作用域变量会在内部作用域可见,但也不要在这个内部的局部作用域中直接使用,因为函数的目的就是为了封装,尽量与外界隔离
  • 如果函数需要使用外部全局变量,请尽量使用函数的形参定义,并在调用传实参解决
  • 一句话,不用global。

闭包

自由变量:未在本地作用域中定义的变量。例如定义在内层函数外的外层函数的作用域中的变量

闭包:就是一个概念,出现在嵌套函数中,指的是内层函数引用到了外层函数的自由变量,就形成了闭包。很多语言都有这个概念,最熟悉就是JavaScript。

def counter():
    c = [0]
    def inc():
        c[0] += 1 # 报错吗? 为什么 # line 4
        return c[0]
    return inc
foo = counter()                    # line 8
print(foo(), foo())                # line 9
c = 100
print(foo())                       # line 11

nonlocal 语句

nonlocal:将变量标记为不在本地作用域定义,而是在上级的某一级局部作用域中定义,但不能是全局作用域中定义。

def counter():
    count=0
    def inc():
        nonlocal count
        count += 1
        return count
    return inc
foo =counter()

count 是外层函数的局部变量,被内部函数引用。

内部函数使用nonlocal关键字声明 count 变量在上级作用域而非本地作用域中定义。

代码中内层函数引用外部局部作用域中的自由变量,形成闭包,

 函数的销毁

定义一个函数就是生成一个函数对象,函数名指向的就是函数对象。

可以使用del语句删除函数,使其引用计数减1.

可以使用同名标识符覆盖原有定义,本质上也是使其引用计数减一

python 程序结束时,所有对象销毁。

函数也是对象,也不例外,是否销毁,还是看引用计数是否减为0.

变量名解析原则LEGB

  • local,本地作用域、局部作用域的local命名空间。函数调用时创建,调用时结束消亡
  • Enclosing , python2.2 时引入了嵌套函数,实现了闭包,这个就是嵌套函数的外部函数的命名空间
  • Global,全局作用域,即一个模块的命名空间。模块被import时创建,解释器退出时消亡
  • Build-in,内置模块的命名空间,生命周期从python解释器启动时创建到解释器退出时消亡。例如 print(open),print和open都是内置的变量

 

内建函数函数签名说明
iteriter(iterable)把一个可迭代对象包装成迭代器
nextnext(iterable[,default])

取迭代器下一个元素

如果已经取完,继续抛stopiteration异常

reversedreversed(seq)返回一个翻转元素的迭代器
enumerateenumerate(seq,start=0)迭代一个可迭代对象,返回一个迭代器,每一个元素都是数字和元素构成的二元组

迭代器

  • 特殊的对象,一定是可迭代对象,具备可迭代对象的特征
  • 通过iter方法把一个可迭代对象封装成迭代器
  • 通过next方法,获取迭代器对象的一个元素
  • 生成器对象,就是迭代器 对象。但是迭代器对象未必是生成器对象

可迭代对象

  • 能够通过迭代一次次返回不同的元素的对象
  • 所谓相同,不是指值是否相同,而是元素在容器中是否是同一个,例如列表中值是可以重复的,['a', 'a'],虽然这个列表有2个元素,值一样,但是两个'a'是不同的元素
  • 可以迭代,但是未必有序,未必可以索引
  • 可迭代对象有:listtuplestringbytesbytearrayrangesetdict、生成器、迭代器等
  • 可以使用成员操作符in、not in
  • 对于线性数据结构,in本质上是在遍历对象,时间O(n)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值