目录

  • 一、函数嵌套调用
    • 1.1函数嵌套调用
    • 1.2 函数嵌套定义
  • 二、名称空间
    • 2.1 名称空间说明
    • 2.2 名称空间的分类
  • 三、作用域
    • 3.1 变量作用域
      • 3.1.1 参数名称的另一种定义形式:
    • 3.2全局作用域
    • 3.3 局部作用域
  • 四、函数对象
    • 4.1 函数对象说明
    • 4.2 函数对象应用(重点)
  • 五、闭包函数
    • 5.1 闭包函数说明
    • 5.2 为函数体传值的两种方式
    • 5.3 nonlocal关键字
  • 六、装饰器
    • 6.1 装饰器说明
    • 6.2 无参装饰器
  • 总结

一、函数嵌套调用

1.1函数嵌套调用

函数嵌套指的是在函数内部又调用了其它的函数。

# 求三个数的最大值
def max2(x, y):if x > y:return xelsereturn ydef max3(x, y, z):res1 = max2(x, y)res2 = max2(res1, z)return res2print(max3(11, 199, 2))

1.2 函数嵌套定义

函数的嵌套定义指的是在函数内又定义了其它函数。

def func1():print('from func1')def func2():print('from func2')print(func2)  # 输出函数func2的地址func2()

二、名称空间

2.1 名称空间说明

名称空间从字面意思理解就是存放名字的地方,在学习变量的时候知道:定义一个变量就是开辟一块内存空间,这个内存空间存放的就是变量的值,除了变量值之外,还有变量名。

变量名和变量值的绑定关系这个数据要在内存中存储。变量名是名字,函数名也是名字,名称空间就是存放名字与值的绑定关系的地方。

2.2 名称空间的分类

名称空间分为三类

  1. 内置名称空间:存放python解释器自带的名字,在解释器启动时就生效,解释器关闭就失败。
  2. 全局名称空间:文件级别的名字,在执行文件的时候生效,在文件结束或者在文件执行时被删除则失效。有些全局名称空间可能一眼看上去不像全局名称空间。我们只需要记住,只要不是内置名称空间和局部名称空间,那么就是全局名称空间。
  3. 局部名称空间:存放函数定义的名称(函数的参数以及函数内的名字都存放于局部名称空间),在函数调用时临时生效,函数结束则失效。
# 内置
print(print)
print(len)#全局
x = 1  # 全局def func():  # func是全局名称空间name = 'Albert'  # name是局部名称空间if 10>3:y = 2  # 全局while True:z = 5  # 全局break

内置名称空间与全局名称空间的结束的生命周期基本上是一致的,程序执行结束或者文件关闭(手动强制关闭写python代码的这个文件),内置或者全局名称空间生命周期结束。局部名称空间生命周期是从函数调用开始到函数结束,即函数生命周期终止。

加载顺序:内置名称空间->全局名称空间->局部名称空间
查找名字:局部名称空间->全局名称空间->内置名称空间

# 加载顺序很好理解,查找顺序是以当前位置为起始点len = 0def f1():len = 1def f2():len = 2print(len)len = 3f2()f1()  # 输出为2

三、作用域

3.1 变量作用域

变量作用域是指一个变量可以使用的范围,例如在函数中定义的变量只允许本函数使用,称之为局部变量;而在代码非函数中定义的变量则称为全局变量,允许在多个函数或者代码中共同访问。

Python为了进行全局变量的标注,提供了global关键字,只需要在变量使用前利用global进行声明就可以自动将函数中操作的变量设置为全局变量。

关于变量名称解析的LEGB原则

  • L(Local):函数内部变量名称;
  • E(Enclosing Function Locals):外部嵌套函数变量名称;
  • G(Global):函数所在模块或程序文件的变量名称;
  • B(Builtin):内置模块的变量名称;

如果按照以上顺序都无法找到指定的变量名称,那么在程序执行时就会报错。

3.1.1 参数名称的另一种定义形式:

在代码开发中常常会出现同一变量名被重复使用的情况,为避免使用时出现问题,项目开发定义变量时往往使用一些标记结合变量名称一起进行定义,例如:

  1. 函数局部变量(本地变量,使用local_var_作为前缀):local_var_name;
  2. 函数参数(使用function_parameter_作为前缀):function_parameter_name;
  3. 全局变量(字母大写,使用GLOBAL_VAR_作为前缀):GLOBAL_VAR_NAME;

采用此类方法可以从根本上杜绝变量名重名的影响。而是否采用这种命名方式,还需要取决于开发者所处的开发公司的命名要求来决定。

3.2全局作用域

全局作用域包含的是内置名称空间与全局名称空间的名字,它的特点是:

  1. 在任何位置都能够访问到;
  2. 该范围的名字会伴随程序整个生命周期;

如果在局部使用全部作用域的变量是没有问题的,但是如果在局部修改全局作用域的变量则不能直接修改,而要使用global关键字才能修改。

global_count = 0def global_check():print(global_count)  # 直接使用全局变量def global_modify():global global_count  # 修改前需要先使用globalglobal_count += 1print(global_count)global_check()
global_modify()

3.3 局部作用域

局部作用域包含的是局部名称空间的名字,它的特点是

  1. 只能在函数中使用;
  2. 调用函数时生效,调用结束失效;

如果在局部使用的是嵌套在函数内部的局部变量,可以直接使用,而修改需要使用nonlocal关键字。

 def make_counter():count = 0def check_counter():print(count)check_counter()def modify_counter():nonlocal countcount += 1print(count)modify_counter()make_counter()

四、函数对象

4.1 函数对象说明

函数在python中是第一类对象,这个话可以通俗理解为函数也是一个对象,就是int,字符串,列表和字典一样都是对象,等学到面向对象就能有进一步的理解,现在可以暂时理解为函数对象可以像int或者字符串一样使用。

# 1、函数可以被引用# int示例
x = 1
y = x# 函数示例
def bar():print('from bar')
f = bar
f()# 2、可以当参数传入# int示例
x = 1def func(a):print(a)func(x)# 函数示例
def bar():print('from bar')def wrapper(func):func()wrapper(bar)# 3、可以当函数的返回值# int示例
x = 1def foo()return xres = foo()
print(res)# 函数示例
def bar()print('from bar')def foo(func)return funcf = foo(bar)
f()# 4、可以当容器类型的元素# int示例
z = 1
l = [z, ]print(l)# 函数示例def get():print('from get')def put():print('from put')l1 = [get, put]l1[0]{}

4.2 函数对象应用(重点)

利用这一特性,可以优雅的取代原来的if多分支(elif这种多分支是我们写代码要尽可能避免的)。

def auth():print('登录。。。。')def register():print('注册。。。。')def check():print('查看。。。。')def transfer():print('转账。。。。')def pay():print('支付。。。。')fun_dict = {'1':auth,'2':register,'3':check,'4':transfer,'5':pay}def interactive():while True:print("""1 登录2 注册3 查看4 转账5 支付""")choice = input('>>').strip()if choice in fun_dict:fun_dict[choice]()else:print('非法操作')interactive()

五、闭包函数

5.1 闭包函数说明

闭包函数就是定义在函数内部的函数,也就是函数的嵌套定义。根据字面意思理解,闭包函数有两个关键字,分别是封闭和包裹。需要注意的重点是:闭包函数的作用域关系在函数定义阶段就固定死了,与调用位置无关。

def outer():x = 1def inner():# 注意和nonlocal的区别# nonlocal x# x += 1x = 2print('from inner',x)return inner  # outer函数返回inner函数对象f = outer()  # 现在f是一个全局变量,同时是inner函数对象print(f)x = 3  # 这个x = 3并不能改变inner函数外层的x
f()def foo():x = 4  # 这个x = 4同样也不能改变f()  # 全局作用域在任意位置都可以调用foo()

闭包函数可以用外层函数来调用内部的函数,打破了函数的层级限制,与此同时该函数包含对外部函数作用域中名字的引用。

def outer():name = 'Albert'def inner():print('my name is %s' % name)return innerf = outer()
f()

5.2 为函数体传值的两种方式

(1)以参数的形式传入

import requests  # requests模块就是模拟浏览器向目标站点发请求def get(url)response = requests.get(url)  # get方法获取请求返回对象print(response)if response.status_code == 200:  # 200是一个状态码,代表请求成功print(response.text)  # text方法是获取返回对象的内容get('https://www.baidu.com')

(2)以闭包函数的形式

闭包函数就是在函数外再包裹一层作用域,由于这个作用域在外层函数内部,所以只作用在内层函数上。

def outer(url)  # 给外层参数传参相当于 url='https://www.baidu.com'def get():response = request.get(url)if response.status_code == 200:print(response.text)return getbaidu = outer('https://www.baidu.com')
python = outer('https://www.python.org')baidu()
python()

5.3 nonlocal关键字

使用闭包结构的最大特点是可以保持外部函数操作的状态,但是如果要想在内部函数中修改外部函数中定义的局部变量或者参数的内容,则必须使用nonlocal关键字。

def print_data(count):def out(data):nonlocal countcount += 1print("count:{},\ndata:{}".format(count,data))return outoa = print_data(1)
oa("www.baidu")

六、装饰器

6.1 装饰器说明

器指的工具(只要是工具,就应该想到函数),装饰指的是为被装饰对象添加新功能。需要注意的是:项目一旦上线之后,就应该遵循开发封闭的原则。开放封闭指的是对修改函数内的源代码和调用方式是封闭的,对功能的扩展是开放的。

看起来有点矛盾,但这就是我们要做的。在这样的要求下,我们必须找到一种解决方案,能够在不修改一个功能源代码以及调用方式的前提下,为其添加新功能。这就用到了装饰器,它能够在不修改被装饰对象源代码与调用方式的前提下,为被装饰器对象添加新功能。

6.2 无参装饰器

(1)无参装饰器实现过程

无参修饰器指的是装饰器本身没有参数

# 要求:为index函数添加一个统计时间的功能
import time  # 这是一个与时间相关的模块def index():time.sleep(3)  # 睡3秒print('welcome to index page')index()# 版本一(只有index函数可以使用)
import time  # 这是一个与时间相关的模块def index():time.sleep(3)  # 睡3秒print('welcome to index page')start_time = time.time()  # 从1970年开始计时的时间戳
index()
end_time = time.time()
print('run time is %s' % (end_time - start_time))% 版本二(两个函数都可以使用,但是有大量重复代码)
def index():time.sleep(3)  # 睡3秒print('welcome to index page')def home(name):time.sleep(5)print('welcome %s to home page' % name)start_time = time.time()
index()
stop_time = time.time()
print('run time is %s' % (end_time - start_time))start_time = time.time()
home('Albert')
stop_time = time.time()
print('run time is %s' % (end_time - start_time))# 版本三(修改了源函数的调用方式)
import timedef index():time.sleep(3)  # 睡3秒print('welcome to index page')def home(name):time.sleep(5)print('welcome %s to home page' % name)def wrapper(func):start_time = time.time()func()stop_time = time.time()print('run time is %s' % (stop_time - start_time))wrapper(index)# 版本四(使用闭包函数,不修改源函数调用方式)
import timedef index():time.sleep(3)  # 睡3秒print('welcome to index page')def outer(func):def wrapper():start_time = time.time()func()stop_time = time.time()print(stop_time - start_time)return wrapperindex = outer(index)  # 赋值给index覆盖原来的index,index = wrapperindex()  # wrapper()# 版本五(解决原函数返回值无效)
import timedef index():time.sleep(1)print('welcome to index page')return 1  # 假设源函数有一个返回值def outer(func):def wrapper():start_time = time.time()res = func()stop_time = time.time()print(stop_time - start_time)return resreturn wrapperindex = outer(index)
res = index()
print(res)# 版本六(终极版,解决有参函数和无参函数通用的问题)
import timedef index():time.sleep(1)print('welcome to index page')return 1  # 假设源函数有一个返回值def home(name):time.sleep(5)print('welcome %s to home page' % name)def timer(func):  # 装饰器也是一个函数,起一个好听的名字def wrapper(*args, **kwargs):start_time = time.time()res = func(*args, **kwargs)stop_time = time.time()print(stop_time - start_time)return resreturn wrapperindex = timer(index)  # 新的index=wrapper
home  = timer(home)  # 新的home=wrapperhome(name='Albert')  # wrapper(name='Albert')
home('Albert')  # wrapper('Albert')
index()  # wrapper()#  无参装饰器模板
def outer(func):def inner(*args, **kwargs):"""这里写装饰器逻辑:param args:任意位置参数:param kwargs:任意关键字参数:return:一个函数对象"""res = func(*args, **kwargs)return resreturn inner

(2)装饰器语法糖

import time# 装饰器也是一个函数,使用函数必须先定义,所以装饰器放在最上方
def timer(func):def wrapper(*args, **kwargs):start_time = time.time()res = func(*args, **kwargs)stop_time = time.time()print(stop_time - start_time)return res@timer  # 在被装饰对象正上方单独一行添加,相当于执行index=timer(index)
def index():time.sleep(1):print('welcome to index page')return 1

(3)用户认证装饰器

import timecurrent_user = {'username': None}def auth(func):def wrapper(*args, **kwargs):if current_user['username']:print('已经登陆过了')res = func(*args, **kwargs)return resname = input('用户名>>').strip()pwd = input('密码>>').strip()if name == 'Albert' and pwd == '1':print('登录成功')current_user['username'] = nameres = func(*args, **kwargs)return reselse:print('用户名或密码错误')return  wrapper@authdef index():time.sleep(1)print('welcome to index page')@auth
def home(name):time.sleep(2)print('welcome %s to home page' % name)index()
home('Albert')

总结

  1. 知道在局部修改全局作用域的变量需要使用关键字global;
  2. 知道在局部中修改嵌套在函数内部的局部变量需要使用关键字nonlocal;
  3. 知道在嵌套函数中对局部变量重新赋值和修改的区别(是否使用nonlocal);
  4. 知道函数对象的应用,这可以有效减少if-else的使用;
  5. 知道闭包函数的使用;
  6. 知道为函数体传值的两种方式(以参数形式和以闭包形式);
  7. 使用@表示对该函数执行装饰函数;

编程高手之路——闭包函数相关推荐

  1. 通往编程高手之路:《深入理解操作系统》

    亚达斯密在其经济学巨著<国富论>中提出了"看不见的那只手"的概念,意指市场机制对经济发展的作用,作为普通的个人来说我们在平时的生活中可能很难感受到市场机制所发挥的作用, ...

  2. Python编程高手之路——第二章:流程控制

    二.流程控制 2.1 运算方式 计算机的核心部件是CPU,CPU有两个功能,控制和运算: 2.1.1 数学运算 print(a ** b) # 幂 print(a // b) // 取整数 2.1.2 ...

  3. python编程高手之路——函数调用

    目录 一.函数介绍 1.1 函数的定义与调用说明 二.函数的定义 2.1 函数定义说明 2.2 定义函数的三种形式 三.函数的调用 3.1 函数调用说明 3.2 调用函数的三种形式 四.函数返回值 4 ...

  4. Python编程高手之路——第三章:数据类型

    三.数据类型 3.1 数据类型 3.1.1 数字类型int和float 数字类型即变量的值,如age=18,18就是我们保存的值: 变量是用来反映/保存状态以及状态变化的,针对不同的状态应该用不同的数 ...

  5. Python编程高手之路——第一章:用户交互

    一.编程入门 1.编程语言的划分 编程语言分为机器语言.汇编语言.高级语言 1.1 机器语言 机器语言是用二进制数字0和1进行编程,机器语言是人站在计算机的角度去编程,编程开发效率低,程序运行效率高. ...

  6. py函数式编程(高阶函数map/reduce/filter/sorted、闭包函数/返回函数、匿名函数lamber、@装饰器decorator、偏函数functool.partial())

    #py函数式编程.py #高阶函数map/reduce/filter/sorted.闭包函数/返回函数.匿名函数lamber.@装饰器decorator.偏函数functool.partial()# ...

  7. Hbase高手之路 -- 第五章 -- HBase的Java API编程

    Hbase高手之路 – 第五章 – HBase的Java API编程 一. 需求与数据集 某自来水公司,需要存储大量的缴费明细数据,以下截取了缴费明细的一部分内容: 因为缴费明细的数据记录非常庞大,该 ...

  8. 中国第一计算机编程高手横瓜的天才求职之路异常艰辛,天妒奇才呀

    中国第一计算机编程高手横瓜的天才求职之路异常艰辛,天妒奇才呀 横瓜(601069289)  17:17:21 大家如何跳槽? 我这样写如何?年薪要求100万,先付50万,面试地点来我所在的城市,并且是 ...

  9. 想成为编程高手,从基础做起

    很多人想成为编程高手,但是常常他们太急功近利了,经常误入歧途.其实编程的路是不平坦的,你会遇到很多的困难.而这些困难很多情况下是需要你一个人解决的.所有你需要一个准备.当然,我的话可能说得重了点,会吓 ...

最新文章

  1. string和stringstream用法总结
  2. java 线程休眠_百战程序员:java线程的休眠和回复
  3. java指针操作符_rxjava 操作符大全
  4. Python两个版本共存时,命令行升级pip
  5. Python 调用Java
  6. Chapter1-5_Speech_Recognition(Alignment of HMM, CTC and RNN-T)
  7. 论文写作——paper Note
  8. python生成目录树_Python生成目录树
  9. TCP/IP协议详解、TCP三次握手
  10. 【转】【JLINK下载失败,STLINK下载失败万能解决方案】JLINK和STLINK都无法下载时的解决办法,此时芯片并没有报废...
  11. Lync Server外部访问系列PART1:准备边缘
  12. Xcode无法识别真机
  13. Origin绘制带标签热图
  14. wps居中对齐不在中间_wps怎么把字水平居中对齐
  15. java超链接大全_JavaFX超链接
  16. 微习惯--简单到四个
  17. linux源码分析之cpu初始化 kernel/head.s,linux源码分析之cpu初始化
  18. uni-app开发经验分享二十二: uni-app大转盘思路解析
  19. 中文,拼音分词使用练习记录
  20. 为什么女性应该考虑从事网络安全事业?

热门文章

  1. 利用Crontab为Linux定时备份Mysql数据库
  2. 磁盘阵列——RAID0制作方法
  3. NodeJS的环境变量process.env.*
  4. 使用 Docker Stack 部署多服务集群
  5. MySQL 配置错误
  6. iOS与H5交互(WKWbebView)
  7. 熊猫多模式站群 模型handlers流程图
  8. c语言 结构体练习之 实现产品销售记录的相关功能
  9. 【C语言】输入一个整数x并判断x是否存在于数组a中
  10. 搭建Struts2步骤