python函数作用域与闭包
函数的定义
在python中,是用def来创建一个函数,实际上def只是完成了一个类似与赋值的操作---------把一个函数对象赋值给一个变量名,还记得我们之前说过在python中变量名只是一个标识符,相当于起到了一个指针的作用,它没有类型(明确这一点是很重要的),又因为python中的一切皆对象,函数当然也不例外,所以,函数被创建后就可以赋值给任意的变量名,也可以作为参数传递给另外一个函数,也可以作为函数的返回值。下面是相应的代码演示 函数赋值给任意变量名:
这里写图片描述
函数作为参数传递:
def fun(test):
print 'Test will execute'
test()
def test():
print 'hello,I am test'
fun(test)
这里写图片描述
函数对象作为返回值:
def test():
def fun():
print 'I am fun'
return fun
a = test()
a()
这里写图片描述
函数作用域
python中有三种(或四种)域作用域相关的作用域。本地变量、(外层函数的本地变量)、全局变量、内建变量 本地变量就是在一个函数内部的变量,全局变量就是不在特定的函数内的,内建变量比较特殊,它是python在开发时就被设计好的一些变量,我们可以通过builtins模块查看。
这里写图片描述
可以看到,其实这些内建的变量就是写进了builtins这个文件里而已,但是这个文件里没有写builtins,所以我们需要导入builtins模块,才能查看它。 这里还有一个奇怪的变量,我把它单独拿出来说--------------外层函数的本地变量,它是伴随着函数嵌套出现的。我们举一个例子来说明:
def test():
x =
def fun():
y =
上面的x就是所谓的外层函数的本地变量,当然它是相对于内层函数fun而言的,它也是本地变量(test的),但是它所处的作用域又不同于fun中的作用域,所以,如果现在fun中再创建一个x变量,他们是不冲突的。
LEGB规则
谈完了函数的作用域,我们就来谈一谈python中变量名的解析规则。对于一个def语句:
- 变量名分为三个作用域查找:首先是本地(L),之后是函数内(E)(如果有的话),之后是全局(G),最后是内置(B)
- 在默认情况下,变量名赋值会创建或改变本地变量 LEGB图示:
这里写图片描述 因为变量名赋值会创建本地变量,所以我们在函数内部想要改变全局变量的值的时候就不能直接给它赋值了(不考虑全局变量作为参数传递进函数),必须要用到global语句来声明这是一个全局变量:
#! /usr/bin/env python
#-*- coding:utf-8 -*-
x = 1
def test():
global x #在函数内声明x为全局变量
x = 2
test()
print 'x=%d'%x
#结果:x=2
global用于声明一个变量是全局变量。但是有一点小细节需要注意,当全局变量是一个可变对象时,例如一个列表,我们可以直接在函数内部对它进行修改,而不是赋值
a = [,,]
def test():
a.append()
print a
test()
#结果:[1,2,3,4]
这里时对可变对象在原处的修改,而不是赋值!!
闭包
首先还得从基本概念说起,什么是闭包呢?来看下维基上的解释: 在计算机科学中,闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。
上面提到了两个关键的地方: 自由变量 和 函数, 这两个关键稍后再说。还是得在赘述下“闭包”的意思,望文知意,可以形象的把它理解为一个封闭的包裹,这个包裹就是一个函数,当然还有函数内部对应的逻辑,包裹里面的东西就是自由变量,自由变量可以在随着包裹到处游荡。当然还得有个前提,这个包裹是被创建出来的。 在通过Python的语言介绍一下,一个闭包就是你调用了一个函数A,这个函数A返回了一个函数B给你。这个返回的函数B就叫做闭包。你在调用函数A的时候传递的参数就是自由变量。 举个栗子:
def func(name):
def inner_func(age):
print 'name:', name, 'age:', age
return inner_func
bb = func('the5fire')
bb() # >>> name: the5fire age: 26
这里面调用func的时候就产生了一个闭包——inner_func,并且该闭包持有自由变量——name,因此这也意味着,当函数func的生命周期结束之后,name这个变量依然存在,因为它被闭包引用了,所以不会被回收。
另外再说一点,闭包并不是Python中特有的概念,所有把函数做为一等公民的语言均有闭包的概念。不过像Java这样以class为一等公民的语言中也可以使用闭包,只是它得用类或接口来实现。
闭包与装饰器
其实装饰器就是闭包的一种应用,下面来引用廖老师教程中的一个例子:
def log(func):
def wrapper(*args, **kw):
print 'call %s():' % func.__name__
return func(*args, **kw)
return wrapper
上面的log函数就是一个装饰器。它接受一个函数参数,我们使用python的@语法,把装饰器放在函数的定义处,这样当执行now函数的时候,就会自动执行log函数。
@log
def now():
print '2013-12-25'
now()
以上程序的输出结果为:
call now():
-12-25
闭包的作用
闭包的最大特点是可以将父函数的变量与内部函数绑定,并返回绑定变量后的函数(也即闭包),此时即便生成闭包的环境(父函数)已经释放,闭包仍然存在,这个过程很像类(父函数)生成实例(闭包),不同的是父函数只在调用时执行,执行完毕后其环境就会释放,而类则在文件执行时创建,一般程序执行完毕后作用域才释放,因此对一些需要重用的功能且不足以定义为类的行为,使用闭包会比使用类占用更少的资源,且更轻巧灵活。
- 表格列的hover状态与选中状态
- 神级程序员教你如何写代码——十年编程内功心法
- ASP.NET Core中的依赖注入(1):控制反转(IoC)
- Web-Fontmin -- 在线提取你需要的字体
- 让Kaggle比赛第二名获奖者告诉你:买下一个冰淇淋的最佳时间是什么时候?
- 3种方式提升云可扩展性
- 数字图像相关技术DIC分析介绍
- 3种提升云可扩展性的方法
- 用一个命令使用 Docker Compose 安装Mesos
- C语言/C加加新手入门学习经验资料分享,基础知识大汇总!
- 云数据服务蜂拥而至...好难选呀
- ASP.NET MVC的Razor引擎:View编译原理
- ASP.NET Core中的依赖注入(4): 构造函数的选择与服务生命周期管理
- 通过Knockout.js + ASP.NET Web API构建一个简单的CRUD应用
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 虚拟机字节码执行引擎,JVM的马达图,是爱情呀
- 微信小程序【浅提WXSS样式】
- Kubernetes Liveness and Readiness Probes
- Magicodes.IE 2.3重磅发布——.NET Core开源导入导出库
- pytest文档59-运行未提交git的用例(pytest-picked)
- pytest文档57-单元测试代码覆盖率(pytest-cov)
- pytest文档58-随机执行测试用例(pytest-random-order)
- Kubernetes探针踩坑记
- 大揭秘| 我司项目组Gitlab Flow && DevOps流程
- 离线安装Superset 0.37(截图详细版)
- 如何高速转储、索引和第7层网络流量过滤?
- 爬虫 | JS逆向某验滑动加密(二)
- 闲聊 Kotlin-Native (0) - 我们为什么应该关注一下 Kotlin Native?
- 哈佛大学单细胞课程|笔记汇总 (五)
- 通过源码理解IGMP v1的实现(基于linux1.2.13)