Python中作用域和闭包

作用域和闭包

作用域
python作用域(重要概念)

python中的作用域分为全局作用域和局部作用域

当我们定义一个函数时,函数之内的函数就是局部作用域
局部作用域中的变量成为局部变量,函数之外就是全局作用域
全局作用域中的变量就是全局变量

作用域的规则:
1、全局作用域的变量,在程序被销毁的时候,全局变狼就会被丢弃
2、局部作用域的变量,在局部运行完后,局部变量也就会被丢弃。
3、全局作用域代码不能访问局部变量,而局部作用域中的代码可以访问全局变量
4、不同局部作用域中的代码不能使用其他局部作用域中的变量
5、不同作用域中,可以使用相同的变量名来表示不同得到变量。

隐式创建局部变量
示例:

b = 10
def f2(a):
    print(a)
    print(b)
    b = 6
f2(3)
输出 :3

b会报错。会被判断成局部变量。因为变量b在函数体内进行了赋值操作

主要是因为Python不要求使用特定的语法进行变量声明,所有它假定在函数定义体中的赋值的变量是局部变量

怎样判断以上所说的内容是正确的呢?

引出反汇编Python函数字节码
示例:from dis import dis
dis(f2)

得出两个关键词 :
LOAD_FAST(局部变量)
LOAD_GLOBAL(全局变量)

可变对象与不可变对象:
示例:

b = []
      def f3(a):
        print(a)
        print(b)
        b.append(1)
    f3(3)
    s输出:3
以上不会报错

解释:此时的变量b指向的是一个列表,利用了列表是可变对象这个特性,而python只会将局部作用域中的赋值操作认为是创建局部变量。我们需要改变不可变对象,除了为变量赋新值,别无他法。

global关键字
如果在函数中赋值时,想让python解释器把变量认为是全局变量,则使用global进行声明。
示例:

b = 10
def f(a):
    global b
    print(a)
    print(b)
    b = 6
f3(3)
输出:3
      10

闭包
闭包概念
(当涉及到函数定义的嵌套时,闭包就出现了)
所谓闭包延伸了作用域的函数,其中包含了函数定义体中引用但不在函数定义体中的非全局变量
!注意,闭包是一个函数,这个函数的一些特性让其变为了闭包,一个函数是不是闭包,关键在于他是否能访问定义体之外定义的非全局变量

示例:

def f():
         a =1
         def f2():
            print(a)
         return f2
    func = f()
    func()

以上8,9,10称之为闭包
在函数f2中,使用了函数定义体外的变量a,此时变量a对于函数f2而言就是一个自由变量,而它们一同构成的结构就是闭包

利用闭包解决问题
示例:

#定义函数体
In [1]: def make_avg():
          #定义一个自由变量
   ...:     values = []
        #定义新的函数体avg,传入参数new_value
   ...:     def avg(new_value):
   ...:         values.append(new_value)
   ...:         return sum(values) / len(values)
   ...:     return avg
   ...:

In [2]: avg = make_avg()

In [3]: avg(5)
Out[3]: 5.0

In [4]: avg(6)
Out[4]: 5.5

In [5]: avg(7)
Out[5]: 6.0

In [6]: avg(8)
Out[6]: 6.5

进一步了解闭包:
什么叫做编译后的函数体

avg.__code__编译后的函数定义体
avg.__code__.co_varnames表示局部变量
('new_value',)
avg.__code__.co_freevars表示自由变量
('values',)
avg.__closure__中的元素对应这co_freevars中的名称
 ('values',):(<cell at 0x000001BF674745B8: list object at 0x000001BF675F4A48>,)
#__closure__元素中有cell_contents属性拥有存放自由变量具体的值
 avg.__closure__[0].cell_contents
[5, 6, 7, 8]

nnonlocal关键字:
问题示例:

In [9]: def make_avg():
   ...:     count = 0  #count = count + 1
   ...:     total = 0  #total = total + 1
   ...:     def avg(new_value):
   ...:         count += 1
   ...:         total += new_value
   ...:         return total / count
   ...:     return avg
   ...:

In [10]: avg = make_avg()


In [11]: avg(5)
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-11-d6bf7ac334bd> in <module>
----> 1 avg(5)

<ipython-input-9-62b76712d46e> in avg(new_value)
      3     total = 0
      4     def avg(new_value):
----> 5         count += 1
      6         total += new_value
      7         return total / count

UnboundLocalError: local variable 'count' referenced before assignment

错误因为:
1、赋值操作会把count变为局部变量,totaly也是一样的
2、python将count变量隐式创建为局部变量,让错误发生

解决问题:
添加nonlocal关键字。该关键字的作用是吧变量标记为自由变量,就算在函数中为变量赋了新的值,也会变为自由变量

正确示例:

In [9]: def make_avg():
   ...:     count = 0  #count = count + 1
   ...:     total = 0  #total = total + 1
   ...:     def avg(new_value):
        添加:   nonlocal count,total
   ...:         count += 1
   ...:         total += new_value
   ...:         return total / count
   ...:     return avg
   ...:

In [10]: avg = make_avg()

In [11]: avg(5)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值