一、作用域

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

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

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

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

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

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

举例说明:

#!/usr/bin/env python3

# -*- coding: utf-8 -*-

__author__ = "问道编程"

__date__ = "2018-06-13 15:40"

passline = 60 #global var

def func(val):

passline = 90 #for func, it"s local var;for in_func, it"s enclosing var

if val >= passline:

print "pass"

else:

print "fail"

def in_func():

print val

return in_func

def Max(val1, val2):

return max(val1, val2) # max is a built-in fun,函数也可以赋值给变量,所以所有的内置函数的作用域都可视作B

声明全局变量:

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)外部函数必须返回内嵌函数——必须返回那个内部函数

三、闭包的使用场景

第一个例子:

#!/usr/bin/env python3

# -*- coding: utf-8 -*-

__author__ = "问道编程"

__date__ = "2018-06-13 15:53"

def funx():

x=5 # 对于funx,是L作用域,对于funy,是E作用域

def funy(): # 是一个闭包

nonlocal x # 绑定到外部的x,只在python3中使用

x+=1

return x

return funy

a = funx()

print(a())

print(a())

print(a())

print(x)

-------------------------

6

7

8

NameError: name "x" is not defined

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

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

def func_150(val): # 总分为150时,及格分数为90

passline = 90

if val >= passline:

print "pass"

else:

print "fail"

def func_100(val): # 总分为100时,及格分数为60

passline = 60

if val >= passline:

print "pass"

else:

print "fail"

func_100(89)

func_150(89)

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

def set_passline(passline):

def cmp(val):

if val >= passline:

print "pass"

else:

print "fail"

return cmp

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)

第三个例子:

def my_sum(*arg):

print("in my_sum,arg=",arg)

return sum(arg)

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)

return in_dec

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()

第四个例子:

#!/usr/bin/env python3

# -*- coding: utf-8 -*-

__author__ = "问道编程"

__date__ = "2018-06-13 17:08"

variable = 300

def test_scopt():

print(variable) # variable是test_scopt()的局部变量,但是在打印时并没有绑定内存对象。

variable = 200 # 因为这里,所以variable就变为了局部变量

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.被装饰函数标识符指向返回的函数对象。

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

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

@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

直接运行该文件,输出:

--------------

in dec

# 此时的my_sum并非指定义的def my_sum(*arg)函数,而是变成接收了装饰器的返回值,in_dec函数。

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

接下来,执行语句:

result = my_sum(1, 2, 3, 4, 5)

print(result)

------------

in dec

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作用域的理解-python中对变量的作用域LEGB、闭包、装饰器基本理解相关推荐

  1. SIGIA_4P python学习 列表 字典 集合 面对对象编程 闭包 装饰器 函数式编程 作用域 异常处理

    SIGIA_4P python学习 列表 字典 集合 面对对象编程 闭包 装饰器 函数式编程 作用域 异常处理 本文连接 简介 SIGIA_4P 网址 a. 课程OKR Objectives and ...

  2. Python(三)对装饰器的理解

    装饰器是 Python 的一个重要部分,也是比较难理解和使用好的部分.下面对装饰器做一下简单整理 1. 前言 装饰器实际上是应用了设计模式里,装饰器模式的思想: 在不概念原有结构的情况下,添加新的功能 ...

  3. python装饰器哪个好_[Python] 对 Python 装饰器的理解心得

    最近写一个py脚本来整理电脑中的文档,其中需要检校输入的字符,为了不使代码冗长,想到使用装饰器. 上网搜索有关python的装饰器学习文档,主要看的是AstralWind的一篇博文,以及Limodou ...

  4. python装饰器-如何理解Python装饰器?

    我从以下几点,由浅入深详细讲解一下Python装饰器:什么事装饰器? 为什么用装饰器? 在哪里用装饰器? 然后以示例+讲解相结合的方式阐述,同时会讲解一些在很多教程和书籍中不会涉及到的内容. 什么是P ...

  5. 趁着课余时间学点Python(九)函数的进阶 生成器 装饰器的理解

    文章目录 前言 生成器 为什么使用 格式 嵌套函数 装饰器 使用场景 使用 结语 前言 相信大家已经对函数有了一定的理解了吧,那么来看看生成器和装饰器吧 生成器 按道理来说,生成器应该是和迭代器一起讲 ...

  6. python入门day11闭包装饰器

    目录 闭包 例子 同级闭包 装饰器引入 装饰器使用 无参例子 有参例子 可变参数例子 带关键字参数的装饰器 双层装饰器 装饰器带参数 装饰器的应用 闭包 def func():a=100def inn ...

  7. python高阶函数闭包装饰器_Python_基础_(装饰器,*args,**kwargs,高阶函数,函数闭包,函数嵌套)...

    一,装饰器 装饰器:本质就是函数,功能是为其它的函数动态添加附加的功能 原则:对修改关闭对扩展开放 1.不修改被修饰函数的源代码 2.不修改被修改函数的调用方式 装饰器实现的知识储备:高阶函数,函数嵌 ...

  8. python装饰器与闭包_python中闭包和装饰器的理解(关于python中闭包和装饰器解释最好的文章)。...

    转载:http://python.jobbole.com/81683/ 呵呵!作为一名教python的老师,我发现学生们基本上一开始很难搞定python的装饰器,也许因为装饰器确实很难懂.搞定装饰器需 ...

  9. python闭包和装饰器的区别_python中闭包和装饰器的理解(关于python中闭包和装饰器解释最好的文章)。(转)...

    呵呵!作为一名教python的老师,我发现学生们基本上一开始很难搞定python的装饰器,也许因为装饰器确实很难懂.搞定装饰器需要你了解一些函数式编程的概念,当然还有理解在python中定义和调用函数 ...

最新文章

  1. C#趣味程序---个位数为6,且能被3整出的五位数
  2. 基于OpenCV的条形码区域分割
  3. python多线程的几种方法
  4. Linux 环境下NFS 服务搭建
  5. [2018雅礼集训1-16]方阵
  6. Java自学笔记(13):【面向对象】方法覆盖,final关键字,对象转型
  7. UDT协议实现分析——连接的建立
  8. 『数学』你确定你学会了勾股弦定理!真的吗?看完这个篇文章再回答我!
  9. redis出现过多command 慢查询slowlog出现command命令
  10. python做线性回归统计推断提取参数_概率分析方法与推断统计(来自我写的python书)...
  11. 旷视科技IPO过会,AI技术“立业”难言轻松
  12. python二维向量运算模拟_Python数学基础之向量定义与向量运算(附代码)
  13. Java继承中为什么不可以降低父类重写方法的访问权限
  14. 常用图表的用法-分布类
  15. 寻找中项和第k小元素c语言,寻找一个序列中第k小的元素——分治法
  16. 电脑上没有tts信息服务器,TTS——让你的电脑会说话-win7 tts
  17. 安规电容知识详解,X电容和Y电容
  18. 《 种子用户方法论》读书笔记
  19. 数值计算中的overflow and underflow
  20. 出中的意思是什么_回归分析中的“回归”是什么意思?

热门文章

  1. 九型性格心理测试 (From Ulla Zang荣格的个人性格测验题目)
  2. TOR交换机和普通交换机有什么区别?
  3. webview进行下载踩坑记录
  4. win10常用详细快捷键大全
  5. python 公司名称 相似度分析_Python文本相似度分析
  6. Vue 中路由传参(动态路由匹配)
  7. tomcat上部署的solr的移植以及数据的备份与恢复
  8. python的培训学校
  9. 使用 Hexo 快速免费搭建个人网站
  10. 桌面壁纸 Lively wallpape