函数的装饰器可以以某种方式增强函数的功能,如在 Flask 中可使用 @app.route('/') 为视图函数添加路由,是一种十分强大的功能。在表现形式上,函数装饰器为一种嵌套函数,这其中会涉及到闭包的概念。而在嵌套函数之间,外部函数中的变量相对于内部函数而言为自由变量,使用时可能需要借助于 nonlocal 关键字进行声明。

nonlocal 声明

按变量的作用域进行分类,Python 中的变量可分为「全局变量」、「局部变量」以及「自由变量」。一般而言,Python 中使用变量前不需要声明变量,但假定在函数体中赋值的变量为局部变量~除非显示使用 global 将在函数中赋值的变量声明为全局变量!

而自由变量则是存在于嵌套函数中的一个概念~定义在其他函数内部的函数被称之为嵌套函数 nested function ,嵌套函数可以访问封闭范围内(外部函数)的变量。嵌套函数不可以在函数外直接访问。

在 Python 中,非本地变量默认仅可读取,在修改时必须显式指出其为非本地变量~自由变量 nonlocal,全局变量 global。>>> ga = 1

>>> def func():

... nb = 2

... def inner():

... ga += 1

... nb += 2

... print('ga is %s, and nb is %s' % (ga, nb))

... return inner

...

>>> test = func()

Traceback (most recent call last):

...

UnboundLocalError: local variable 'ga' referenced before assignment

未加入全局变量和自由变量声明时且使用赋值操作时,inner 函数的变量 ga, nb 默认为局部变量,会报错;如注释掉 ga += 1 后同样会报错:Traceback (most recent call last):

...

UnboundLocalError: local variable 'nb' referenced before assignment

可行改写如下:>>> ga = 1

>>> def func():

... nb = 2

... def inner():

... global ga

... nonlocal nb

... ga += 1

... nb += 2

... print('ga is %s, and nb is %s' % (ga, nb))

... return inner

...

>>> test = func()

>>> test()

ga is 2, and nb is 4

>>> test()

ga is 3, and nb is 6

通过显示声明 ga, nb 分别为「全局变量」和「自由变量」,此时如预期运行!

闭包

函数内的函数以及其自由变量形成闭包。也即闭包是一种保留定义函数时存在的自由变量的绑定的函数~这样在调用函数时,绑定的自由变量依旧可用。

闭包可以避免全局变量的使用以及提供某种形式的数据隐藏。当函数中的变量和函数较少且其中某个功能常用时,使用闭包来进行封装。当变量和函数更加复杂时,则使用类来实现。# 计算移动平均值的函数

def make_averager():

series = []

def averager(new_value):

series.append(new_value)

total = sum(series)

return total/len(series)

return averager

那么此时,make_averager() 函数的第二行 series = [] 到第七行 return total/len(series) 为闭包,变量 series 为 averager() 函数中的自由变量!# avg 为一个 averager 函数对象 ~ 含自由变量的绑定

>>> avg = make_averager()

>>> avg(10)

10.0

>>> avg(11)

10.5

>>> avg(12)

11

# 创建另一个 averager 函数对象

>>> avg2 = make_averager()

>>> avg2(1)

1.0

>>> avg2(18)

9.5

# 查看 avg, avg2 自由变量中保存的值

>>> avg.__closure__[0].cell_contents

[10, 11, 12]

>>> avg2.__closure__[0].cell_contents

[1, 18]

函数对象通过 __closure__ 属性 —— 返回 cell 对象元祖(函数中有多少嵌套函数则该元祖的长度有多长),生成该对象的函数被称之为闭包函数。

func.__closure__[0].cell_contents: 访问存储在 cell 对象中值。

装饰器

装饰器本身是一个可调用的对象~函数或类,其参数为另一个函数(被装饰的函数)。装饰器可能会处理被装饰的函数(如添加一些功能)然后将之返回,或者将之替换为另一个函数或可调用对象。这也被称之为元编程 metaprogramming —— 在编译时改变函数功能。>>> def make_pretty(func):

... def inner():

... print("I got decorated!", end='')

... func()

... return inner

...

>>> def ordinary():

... print("I am ordinary!")

# 用 make_pretty 函数装饰 ordinary 函数

>>> pretty = make_pretty(ordinary)

>>> pretty()

I got decorated! I am ordinary!

可以作为装饰器的函数内部都有嵌套的功能函数(用以实现主要功能),并返回内部的嵌套函数。@make_pretty

def ordinary():

print("I am ordinary!")

# 等价于

def ordinary():

print("I am ordinary!")

ordianry = make_pretty(ordinary)

make_pretty(func) 是一个最简单的装饰器,它接受一个函数为其参数;内部定义了一个 inner() 函数~输出 "I got decorated!" 后执行被装饰函数(此时 func 为 inner 闭包中的自由变量);然后返回内部函数 inner。

此时,对于被装饰的函数 ordinary 而言,此时是 inner 的引用:>>> ordinary()

I got decorated! I am ordinary!

>>> ordinary

.inner at 0x10aeaa1e0>

除了最简单的装饰器之外,还可以将多个装饰器叠放使用

叠放装饰器def star(func):

def inner(*args, **kwargs):

print('*' * 30)

func(*args, **kwargs)

print('*' * 30)

return inner

def dollar(func):

def inner(*args, **kwargs):

print('$' * 30)

func(*args, **kwargs)

print('$' * 30)

return inner

@star

@doller

def printer(msg):

print(msg)

printer("Hello world!")

# 结果如下

'''

******************************

$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

Hello world!

$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

******************************

'''

# 等价于

def printer(msg):

print(msg)

printer = star(dollar(printer))

python闭包与装饰器有啥关系_深入理解Python中的闭包与装饰器相关推荐

  1. python装饰器使用多吗_如何理解Python装饰器?

    Python中的装饰器是通过利用了函数特性的闭包实现的,所以在讲装饰器之前,我们需要先了解函数特性,以及闭包是怎么利用了函数特性的 ① 函数特性 python中的函数特性总的来说有以下四点: 1. 函 ...

  2. 从编译器层面理解C#中的闭包的这个坑!

    前言 在公众号上看到一篇文章<正确使用和理解C#中的闭包>,里面提到了闭包的一个坑: 当捕获的外部变量为for循环的迭代变量时,C#认为变量i是定义在循环体外的.所以,当添加委托集合的fo ...

  3. 如何用python写数值运算_如何理解Python的数值运算?

    1 基本算术运算 1.1 使用规则 – Python解析器相当于一个简单的计算器 – Python解析器可以接受简单的算术表达式 – 运算符可以使加(+)减(-)乘(*)除(/) 1.2 实操理解 # ...

  4. 【Python】Python实战从入门到精通之一 -- 教你深入理解Python中的变量和数据类型

    本文是Python实战–从入门到精通系列的第一篇文章: Python实战从入门到精通之一 – 教你深入理解Python中的变量和数据类型 文章目录 1.变量 1.1 变量命名规则 1.2 变量名称错误 ...

  5. python装饰器的通俗理解_简单理解Python装饰器

    Python有大量强大又贴心的特性,如果要列个最受欢迎排行榜,那么装饰器绝对会在其中. 刚接触装饰器,会觉得代码不多却难以理解.其实装饰器的语法本身挺简单的,复杂是因为同时混杂了其它的概念.下面我们一 ...

  6. python装饰器由浅入深_由浅入深理解Python装饰器

    前提知识: 1.Python里函数也是一种对象: def shout(word="yes"): return word.capitalize()+"!" pri ...

  7. python装饰器理解_如何理解Python装饰器?

    首先,本垃圾文档工程师又来了.开始日常的水文写作.起因是看到这个问题如何理解Python装饰器?,正好不久前给人讲过这些,本垃圾于是又开始新的一轮辣鸡文章写作行为了. 预备知识 首先要理解装饰器,首先 ...

  8. 什么是python装饰器_深入理解 Python 装饰器

    作者简介 曾凡伟,携程信息安全部高级安全工程师,2015年加入携程,主要负责安全自动化产品的设计和研发,包括各类扫描器.漏洞管理平台.安全 SaaS 平台等. Python 是一门追求优雅编程的语言, ...

  9. python中装饰器的作用_如何理解Python装饰器

    展开全部 理解Python中的装饰器 @makebold @makeitalic def say(): return "Hello" 打印出如2113下的输出: Hello 你会怎 ...

最新文章

  1. 移动端zepot媒体查询media queries
  2. nonatomic与atomic的区别与作用
  3. bugfree3.0.2版本升级--更新版
  4. 百度地图与HT for Web结合的GIS网络拓扑应用
  5. (六)docker-compose使用教程
  6. User status profile field in WebUI
  7. .net 导出excel_Qt编写的项目作品18-数据导出到Excel及Pdf和打印数据
  8. java筛选法求质数_用筛选法找出N内所有素数(质数)
  9. curl post请求 header host_(科普文)curl quot;可quot;得一切
  10. 动态html树形菜单模板,JS+CSS简易树状菜单Tree
  11. row_number()over函数的使用(转)
  12. (CSCD 理工科)中文科技核心期刊汇总
  13. 客户服务器与p2p文件分发,P2P大文件分发技术 | 点量软件
  14. 微软云服务器搭建,75分钟快速构建微软Server 2012私有云
  15. PS 插件批量压缩png图片
  16. 美团外卖移动端性能监测体系实现
  17. java if 跳出循环_break跳出的是if语句,还是for循环?
  18. 爱创课堂告诉你为什么别人年薪20万,你只是一个小码农!
  19. Linux之网络配置
  20. HTTPS hostname wrong: should be <

热门文章

  1. 利用python爬虫(案例2)--X凰的一天
  2. python中socket实现代理功能_python实现代理服务功能实例
  3. ppp协议pap验证过程状态转移图_硬核分享|Crust 核心协议栈的设计与实现
  4. OData metadata 定义中,entity type key 的作用是什么
  5. SAP Commerce Cloud 架构概述
  6. 回答一位朋友关于 SAP SRM 行业 转 CRM 行业的咨询
  7. Angular Reactive Form里的setNgReflectProperty
  8. function implemented in Scala - compiled java code - some closure example
  9. SAP Business Application Studio的权限控制
  10. SAP UI5 Opportunity type long description empty issue