一、作用域

  在Python程序中创建、改变、查找变量名时,都是在一个保存变量名的空间中进行,我们称之为命名空间,也被称之为作用域。python的作用域是静态的,在源代码中变量名被赋值的位置决定了该变量能被访问的范围。即Python变量的作用域由变量所在源代码中的位置决定。

  在Python中并不是所有的语句块中都会产生作用域。只有当变量在Module(模块)、Class(类)、def(函数)中定义的时候,才会有作用域的概念。而在if、for、while等语句中不产生作用域。

  L(local)局部作用域,存在于def内的局部性的作用域,随着函数的定义而产生。

  E(enclosing)嵌套作用域,同L作用域是相对而言,存在函数嵌套时,内部函数的作用域称为L作用域,外部函数称为E作用域。

  G(global)全局作用域,变量在该模块内的作用域,可以简单的理解为单个文件内的范围。

  B(built-in)内置作用域,系统内固定模块里定义的变量,如预定义在builtin 模块内的变量,最高级别的作用域,范围最广,可以理解为当前运行系统的范围。

举例说明:

  1. #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    __author__ = "问道编程"
    __date__ = "2018-06-13 15:40"
  1. passline = 60 #global var
  2.  
  3. def func(val):
  1. passline = 90 #for func, it's local var;for in_func, it's enclosing var
  1. if val >= passline:
    print "pass"
    else:
    print "fail"
    def in_func():
    print val
    return in_func
  2.  
  3. def Max(val1, val2):
    return max(val1, val2) # max is a built-in fun,函数也可以赋值给变量,所以所有的内置函数的作用域都可视作B

声明全局变量:

  1. a = 100
    def foo():
    global a # 引用全局变量a,并认可其修改的值
    a = 200
    print(a)
    foo()
    print(a)
    ---------
    100
    200

二、闭包

  首先理解下python中的函数,在python中,函数是一个对象(可以通过type函数查看),在内存中占用空间;函数执行完成之后内部的变量会被解释器回收,但是如果某变量被返回,则不会回收,因为引用计数器的值不为0;既然函数也是一个对象,他也拥有自己的属性;对于python函数来说,返回的不一定是变量,也可以是函数。

  由此引出闭包的概念,当存在函数嵌套时(如上例中的func()和in_func()就是嵌套关系),外部函数的返回值为内部函数,且内部函数引用外部函数的变量、参数,并将引用的变量、参数封装在函数中一起作为返回值,其中的内部函数就称为一个闭包。

  形成闭包的必要条件:

1)必须有一个内嵌函数(函数里定义的函数)——这对应函数之间的嵌套
2)内嵌函数必须引用一个定义在闭合范围内(外部函数里)的变量——内部函数引用外部变量
3)外部函数必须返回内嵌函数——必须返回那个内部函数

三、闭包的使用场景

第一个例子:

  1. #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    __author__ = "问道编程"
    __date__ = "2018-06-13 15:53"
  2.  
  3. def funx():
    x=5 # 对于funx,是L作用域,对于funy,是E作用域
    def funy(): # 是一个闭包
    nonlocal x # 绑定到外部的x,只在python3中使用
    x+=1
    return x
    return funy
  4.  
  5. a = funx()
    print(a())
    print(a())
    print(a())
    print(x)
    -------------------------
    6
    7
    8
    NameError: name 'x' is not defined

  本来x只是funx()的局部变量,但是形成了闭包之后,它的行为就好像是一个全局变量一样,最后的错误说明x并不是一个全局变量。其实这就是闭包的一个十分浅显的作用,形成闭包之后,闭包变量能够随着闭包函数的调用而实时更新,就好像是一个全局变量那样。

第二个例子,也是讲闭包用的最多的例子:

  1. def func_150(val): # 总分为150时,及格分数为90
    passline = 90
    if val >= passline:
    print "pass"
    else:
    print "fail"
  2.  
  3. def func_100(val): # 总分为100时,及格分数为60
    passline = 60
    if val >= passline:
    print "pass"
    else:
    print "fail"
  4.  
  5. func_100(89)
    func_150(89)

使用闭包优化上面的代码:

  1. def set_passline(passline):
    def cmp(val):
    if val >= passline:
    print "pass"
    else:
    print "fail"
    return cmp
  2.  
  3. f_100 = set_passline(60) # f_100调用函数set_passline(),并将60赋值给变量passline,这是f_100等于函数的返回值,也就是函数cmp
    f_150 = set_passline(90)
    f_100(89) # f_100()=cmp(),将89赋值给val,运行cmp()函数,输出结果
    f_150(89)

第三个例子:

  1. def my_sum(*arg):
    print('in my_sum,arg=',arg)
    return sum(arg)
  2.  
  3. def dec(func):
    def in_dec(*arg):
    print('in in_dec,arg=', arg)
    if len(arg) == 0:
    return 0
    for val in arg:
    if not isinstance(val, int):
    return 0
    return func(*arg)
  4.  
  5. return in_dec
  6.  
  7. my_sums = dec(my_sum) # 命名为my_sums,是为了和my_sum进行区分,便于理解
    # ①调用dec()函数,将dec的返回值赋值给my_sums,相当于my_sums=in_dec,②将函数my_sum赋值给func,相当于func=my_sum
    result = my_sums(1, 2, 3, 4, 5)
    # ③相当于将(1, 2, 3, 4, 5)赋值给in_dec函数中的arg,调用并执行函数in_dec(),
    # ④in_dec()函数的return返回值是func()函数,将in_dec函数中的arg赋值给func()函数中的arg,也就是赋值给my_sum()中的arg
    # ⑤调用并执行函数my_sum(),将sum(arg)结果返回给变量result
    print(result)
    ------------
    in in_dec,arg= (1, 2, 3, 4, 5)
    in my_sum,arg= (1, 2, 3, 4, 5)
    15
    整个过程可以简单的理解为,先运行dec()函数,其返回值是in_dec函数,再运行in_dec函数,其返回值为func函数,也就是my_sum函数,再运行my_sum函数
    dec() -> in_dec() -> my_sum()

第四个例子:

  1. #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    __author__ = "问道编程"
    __date__ = "2018-06-13 17:08"
  2.  
  3. variable = 300
  4.  
  5. def test_scopt():
    print(variable) # variable是test_scopt()的局部变量,但是在打印时并没有绑定内存对象。
    variable = 200 # 因为这里,所以variable就变为了局部变量
  6.  
  7. test_scopt()
    print(variable)
    ------------
    UnboundLocalError: local variable 'variable' referenced before assignment

  上面的例子会报出错误,因为在执行程序时的预编译能够在test_scopt()中找到局部变量variable(对variable进行了赋值)。在局部作用域找到了变量名,所以不会升级到嵌套作用域去寻找。但是在使用print语句将变量variable打印时,局部变量variable并有没绑定到一个内存对象(没有定义和初始化,即没有赋值)。本质上还是Python调用变量时遵循的LEGB法则和Python解析器的编译原理,决定了这个错误的发生。所以,在调用一个变量之前,需要为该变量赋值(绑定一个内存对象)。
  注意:为什么在这个例子中触发的错误是UnboundLocalError而不是NameError:name ‘variable’ is not defined。因为变量variable不在全局作用域。Python中的模块代码在执行之前,并不会经过预编译,但是模块内的函数体代码在运行前会经过预编译,因此不管变量名的绑定发生在作用域的那个位置,都能被编译器知道。Python虽然是一个静态作用域语言,但变量名查找是动态发生的,直到在程序运行时,才会发现作用域方面的问题

  可以使用global将变量variable声明为全局变量。

 四、装饰器

  概念性的知识,拗口的表述:

1.装饰器就是对闭包的使用;
2.装饰器的作用是装饰函数;
3.装饰器会返回一个函数对象,被装饰的函数接收;
4.被装饰函数标识符指向返回的函数对象。

  将上面第三个例子用上装饰器:

  1. def dec(func):
    def in_dec(*arg):
    print('in in_dec,arg=', arg)
    if len(arg) == 0:
    return 0
    for val in arg:
    if not isinstance(val, int):
    return 0
    return func(*arg)
       print('in dec') # 新增的一行
    return in_dec
  1. @dec
    def my_sum(*arg):
    print('in my_sum,arg=',arg)
    return sum(arg)
    print(type(my_sum))

  对概念的实例化:

1、@dec是一个装饰器,是调用dec()函数形成的装饰器;

2、被装饰的函数是my_sum(),即调用dec()函数形成装饰器时,dec()函数的参数为my_sum()函数,即:func = my_sum

3、装饰器的返回值是函数in_dec(),该返回值被函数my_sum()接收,其实是传给被装饰函数(即my_sum()函数)的标识符my_sum,即:my_sum=in_dec

  直接运行该文件,输出:

  1. --------------
    in dec
    <class 'function'> # 此时的my_sum并非指定义的def my_sum(*arg)函数,而是变成接收了装饰器的返回值,in_dec函数。

  说明只要使用了装饰器,就会调用装饰器里的函数;并且装饰器的返回值:in_dec(),被my_sum接收(如果没有return in_dec,直接运行,则my_sum格式为:<class 'NoneType'>,说明装饰器没有返回值时,会返回None,这一点跟函数一样)。

  接下来,执行语句:

  1. result = my_sum(1, 2, 3, 4, 5)
    print(result)
    ------------
    in dec
    <class 'function'>
    in in_dec,arg= (1, 2, 3, 4, 5)
    in my_sum,arg= (1, 2, 3, 4, 5)
    15

  运行过程为(结合第三个例子进行理解):

1、@dec,调用dec函数,其参数就是被装饰的函数my_sum(),即func=my_sum,执行print('in dec'),返回in_dec函数,被my_sum接收,即:最后会将函数in_dec的运行结果,传给被装饰函数的标识符my_sum;

2、调用in_dec函数,参数为(1,2,3,4,5),执行print、if、for语句,执行return func(*arg),func=my_sum函数;

3、调用函数my_sum(),参数为(1,2,3,4,5),执行print语句,执行return sum(*arg),返回最终结果;

参考文章:

https://www.cnblogs.com/fireporsche/p/7813961.html

https://www.cnblogs.com/cotyb/p/5243252.html

python中对变量的作用域LEGB、闭包、装饰器基本理解的更多相关文章

  1. Python中的变量和作用域详解

    Python中的变量和作用域详解 python中的作用域分4种情况: L:local,局部作用域,即函数中定义的变量: E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部 ...

  2. Python中的变量、引用、拷贝和作用域

    在Python中,变量是没有类型的,这和以往看到的大部分编辑语言都不一样.在使用变量的时候,不需要提前声明,只需要给这个变量赋值即可.但是,当用变量的时候,必须要给这个变量赋值:如果只写一个变量,而没 ...

  3. JavaScript闭包理解【关键字:普通函数、变量访问作用域、闭包、解决获取元素标签索引】

        一.闭包(Closure)模糊概述 之前总觉得闭包(Closure)很抽象而且难理解,百度一下"闭包"名词,百度的解释是:“闭包是指可以包含自由(未绑定到特定对象)变量的代 ...

  4. python中的变量和数据类型

    一.变量定义:变量是计算机内存中的一块区域,存储规定范围内的值,值 可以改变,通俗的说变量就是给数据起个名字. 二.变量命名规则: 1. 变量名由字母.数字.下划线组成 2. 数字不能开头 3. 不可 ...

  5. Python 中的变量

    Python采用基于值得内存管理模式,赋值语句的执行过程是:首先把等号右侧标识的表达式计算出来,然后在内存中找一个位置把值存放进去,最后创建变量并指向这个内存地址.Python中的变量并不直接存储值, ...

  6. Python中的变量引用对象需注意的几点

    Python中的变量引用对象需注意的几点 分类:Python (55)  (0) 普通引用: Python中,变量的作用仅仅是一个标识,只有赋值后才被创建,它可以引用任何类型的对象,而且在引用之前必须 ...

  7. Python中的变量和常量

    本文主要介绍Python中的变量和常量,包括变量的命名规范,使用注意事项 -------------- 完美的分割线 --------------- 1.变量 1.1.变量理解 1)什么是变量 变量即 ...

  8. Python中执行变量而非字符串

    Python中执行变量而非字符串 设想这样的场景,你需要大型项目的开发.但是项目的开发第一步是啥? 当然是import导入了. ...but............ 默认 import 后面跟着字符串 ...

  9. python中的变量与对象

    一. 什么是变量 变量就是以前学习的数学中常见的等式x = 3(x是变量,3是变量值),在编程中,变量不仅可以是数学,还可以是任意数据类型 二. 变量的命名规则 变量名必须是英文大小写.数字和_的组合 ...

随机推荐

  1. 【转】Python之日期与时间处理模块(date和datetime)

    [转]Python之日期与时间处理模块(date和datetime) 本节内容 前言 相关术语的解释 时间的表现形式 time模块 datetime模块 时间格式码 总结 前言 在开发工作中,我们经常 ...

  2. sklearn,交叉验证中的分层抽样

    StratifiedKFold用法类似Kfold,但是他是分层采样,确保训练集,测试集中各类别样本的比例与原始数据集中相同. 例子: import numpy as np from sklearn.m ...

  3. PYTHON-网络通信 TCP

    网络编程: 学习网络编程 为什么?目的: 服务端特点: 网络通讯(通信) 什么是网络通讯? 为什么?目的:网络建立的目的是为数据交互(通信) 如何实现通讯(通信)? 互联网协议 互联网=物理连接介质+ ...

  4. selenium webdriver+python基本操作

    # -*- coding:utf-8 -*-#导入模块from selenium import webdriver from selenium.common.exceptions import NoS ...

  5. [转]安装ambari

    一.准备工作: 基本工具 yumrpmscpcurlwgetpdsh前几个一般系统都自带了,pdsh需要自己装 yum install pdsh 2.配置hosts vim /etc/hosts 10 ...

  6. cf796d 树,bfs好题!

    绝对是好题,把所有警察局放入队列然后开始广搜,如果碰到了vis过的顶点,但是那条边没有访问过,那么这条边就可以删掉 另外广搜的vis标记是在入队时就打的,, #include<bits/stdc ...

  7. hdu2888 二维ST表(RMQ)

    二维RMQ其实和一维差不太多,但是dp时要用四维 /* 二维rmq */ #include<iostream> #include<cstring> #include<cs ...

  8. hdu3255扫描线:带权面积交转体积交

    手贱把i打成j,调了半天 /* 面积并转体积并,长方体高度为作物价格 算体积并:在笛卡尔坐标系的y轴上建立线段树cnt记录区间被完全覆盖的次数,sum记录区间被覆盖的总长度 以平行于xoy的平面从下往 ...

  9. 使用fidder进行接口测试

    官方下载地址 下载后一路next安装即可. get请求 get请求直接把需要携带的参数使用?跟在后面如:http://127.0.0.1:8000/api/get_event_list/?id=1 p ...

  10. python 全栈开发,Day133(玩具与玩具之间的对话,基于jieba gensim pypinyin实现的自然语言处理,打包apk)

    先下载github代码,下面的操作,都是基于这个版本来的! https://github.com/987334176/Intelligent_toy/archive/v1.6.zip 注意:由于涉及到 ...