15 python 初学(闭包,函数装饰器)
这一部分很重要,一定要透彻理解。可参考大神博客:
http://www.cnblogs.com/yuanchenqi/articles/5830025.html
闭包:
如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数被认为是闭包。
闭包 = 函数块 + 定义函数时的环境
inner 是内部函数,对 x 进行引用,内部函数 inner 就是一个闭包
!!!当闭包执行完后,仍然能够保持住当前的运行环境。比如说,如果你希望函数的每次执行结果,都是基于这个函数上次的运行结果。
# 个人理解:下面的 inner()函数不是一个闭包函数,因为他并没有使用 outer 中的外部变量。
# 不理解的一点:在外部直接调用 inner()函数不可行 (是因为找不到),但是执行outer(),将 inner 作为返回值给f,f在外部就可以执行。这又是因为什么呢???
# ------------闭包有一个属性__closure__。 如果 inner 函数没有使用外部变量,那就不属于闭包的范畴。 f.__closure__返回为None。
def outer():
x = 10
def inner():
return 5
return inner f = outer() # f.__closure__返回为None。不是闭包
print(f())
# 下面的 inner()函数是一个闭包函数,因为 inner 函数和外部环境变量 x 结合在了一起。
def outer():
x = 10
def inner():
print(x)
return 5 return inner # f 其实就是 inner,但此处仍然可以执行inner,就是因为inner使用了外部变量 x,在return inner的时候把x也一起捆绑给了他,让他使用。但是如果单独调用inner函数就会报错。
f = outer() # f.__closure__返回为:(<cell at 0x000000FB7467D768: int object at 0x00007FFB0FD7B470>,) print(f())
闭包应用:装饰器
闭包举例:棋盘。参考上面博客链接
开放封闭原则:
封闭原则:代码写好后不允许再进行修改
开放原则:在不修改代码的条件下可以进行功能扩展
装饰器:
# 需求:打印出每个函数执行时间 # 这样写的话,每个需要打印执行时间的函数都要写一遍相同的代码,代码重复。
def foo():
start = time.time()
print('foo....')
end = time.time()
print('spend: %s' %(end - start)) foo() # 改进方法1 :抽出相同的代码,写成函数print_time,将 foo 函数作为参数传入
# 问题:在这种情况下,我不能调用函数本身去实现打印时间了,而是要通过print_time函数。
# 这样如果其它地方之前是调用了这个函数来打印时间,现在改进了之后,就都要改一遍调用方式,很麻烦
def foo():
print('foo....')
time.sleep(2) def print_time(f):
start = time.time()
f()
end = time.time()
print('spend: %s' % (end - start)) print_time(foo) # 改进方法2:在 print_time 函数内定义一个内部函数 inner(),将 print_time 返回这个内部函数,重新赋值给函数变量 foo。
# 这样通过直接调用 foo()就可以实现打印时间的功能了。
# 改进方法3:更加高级一点的方法,使用装饰器(其实我觉得每次写 foo = print_time(foo) 也是一个很浪费时间的重复工作,装饰器可以解决)
# 通过给一个函数使用装饰器,每次就可以直接通过调用函数来实现打印时间的功能了。 def print_time(f):
def inner():
start = time.time()
f()
end = time.time()
print('spend: %s' % (end - start)) return inner @print_time # 等价于语句:foo = print_time(foo)
def foo():
print('foo....')
time.sleep(2) # 改进方法2 部分
# foo = print_time(foo) foo()
带参数的被装饰函数:即 foo 是一个有参数的函数,那么如何写这个装饰器呢?
由浅入深,从简单的两个参数到不定长参数。
两个参数实现举例:需要注意的地方
1. inner(a, b)函数的参数要和foo(a, b)函数的参数保持一致。a, b 参数会自动对应 foo(a, b)的参数
2. inner(a, b)函数内部一定要调用实现 f(a,b)函数。
3. (我自己的理解与想象,未证实)foo 函数 使用装饰器 @print_time 时,因为 print_time 函数定义的有参数 f,因此就会自动帮我们将 foo 函数变量作为参数传入。
def print_time(f):
def inner(a, b):
start = time.time()
f(a, b)
end = time.time()
print('spend: %s' % (end - start)) return inner @print_time # 等价于语句:foo = print_time(foo)
def foo(a, b):
print(a + b)
time.sleep(2) foo(2,3) #
# spend: 2.0001039505004883
不定长参数:
import time def print_time(f):
def inner(*args, **kwargs):
start = time.time()
f(*args, **kwargs)
end = time.time()
print('spend: %s' % (end - start)) return inner @print_time # 等价于语句:foo = print_time(foo)
def foo(*args, **kwargs):
sum = 0
for i in args:
sum += i
print(sum)
time.sleep(2) foo(2, 3, 4, 5)
带参数的装饰器:
被装饰函数可以带参数,那么装饰器可以带参数吗?答案是肯定的。
看一下实现举例(函数直接复制的):
import time def time_logger(flag=0):
def show_time(func):
def wrapper(*args, **kwargs):
start_time = time.time() func(*args, **kwargs) end_time = time.time() print('spend %s' % (end_time - start_time)) if flag:
print('将这个操作的时间记录到日志中') return wrapper return show_time @time_logger(3)
def add(*args, **kwargs):
time.sleep(1) sum = 0 for i in args:
sum += i print(sum) add(2, 7, 5)
在上面这个函数中,装饰器变为了 @time_logger(3),他做的主要工作有哪些呢,即他实现了一个什么功能呢?
1. 执行time_logger(3),得到闭包变量 show_time,里面保存环境变量 flag。
2. 执行 @show_time,相当于实现语句 add = show_time(add)。接下来的逻辑同上
(自己的理解,期待指正):之所以先执行time_logger(3),而不是执行@time_logger(3),是因为判断出 time_logger 函数里面并没有将函数 add 传入进去,而是传入了 3。因此就先执行了time_logger(3),并返回了闭包变量 show_time。
接下里试着执行 @show_time,发现此处没有传参数,但是 show_time 函数定义中却有一个参数 f,因此就会将函数 add 作为实参传给 f。这样就执行了 @show_time
???是不是将 函数add 传入后就是开始执行了装饰器语句 @show_time
等我理解的更深入了会再修改,目前的理解就是这样。
15 python 初学(闭包,函数装饰器)的更多相关文章
- Python作用域-->闭包函数-->装饰器
1.作用域: 在python中,作用域分为两种:全局作用域和局部作用域. 全局作用域是定义在文件级别的变量,函数名.而局部作用域,则是定义函数内部. 关于作用域,我要理解两点:a.在全局不能访问到局部 ...
- Python中利用函数装饰器实现备忘功能
Python中利用函数装饰器实现备忘功能 这篇文章主要介绍了Python中利用函数装饰器实现备忘功能,同时还降到了利用装饰器来检查函数的递归.确保参数传递的正确,需要的朋友可以参考下 " ...
- Python函数编程——闭包和装饰器
Python函数编程--闭包和装饰器 一.闭包 关于闭包,即函数定义和函数表达式位于另一个函数的函数体内(嵌套函数).而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量.参数.当其中一个 ...
- 【Python 函数对象 命名空间与作用域 闭包函数 装饰器 迭代器 内置函数】
一.函数对象 函数(Function)作为程序语言中不可或缺的一部分,但函数作为第一类对象(First-Class Object)却是 Python 函数的一大特性. 那到底什么是第一类对象(Firs ...
- python之闭包与装饰器
python闭包与装饰器 闭包 在函数内部定义的函数包含对外部的作用域,而不是全局作用域名字的引用,这样的函数叫做闭包函数. 示例: #-------------------------------- ...
- Python之闭包and装饰器
闭包和装饰器是Python中非常重要的一种语法格式,在日常工作中应用非常广泛. 首先,我先为大家简单的介绍一下闭包的概念. 闭包:闭包是在函数嵌套的基础上,内层函数使用到外层函数的变量,且外层函数返回 ...
- Python中的@函数装饰器到底是什么?
在解释@函数装饰器之前,先说一下,类中的类方法和静态方法. 在Python中完全支持定义类方法.静态方法.这两种方法很相似,Python它们都使用类来调用(ps:用对象调用也可以). 区别在于:Pyt ...
- python语法基础-函数-装饰器-长期维护
######################################################### # 装饰器 # 装饰器非常重要,面试Python的公司必问, # 原则:开放封闭原则 ...
- python 修改的函数装饰器
把好的代码记录下来 方便以后学习 修改的函数参数装饰器 from functools import wraps import time import logging def warn(timeout) ...
- python二 总结--函数-- 装饰器
装饰器是什么? 有什么用? 为什么要用? 真的有用吗? 1.装饰器: 装饰器: 定义:本质是函数,(装饰其他函数)就是为其他函数添加附加功能. 原则:1.不能修改被装饰的函数的源代码 ...
随机推荐
- jquery中each中使用break和continue
在jquery中each中直接使用break或者continue会提示:必须在循环中使用.会报错不能直接使用. 但是,是不是就不能用呢,答案是的,但是换种方法可以达到相同的效果: 可以只用return ...
- JS中substring与substr的用法
substring方法用于提取字符串中介于两个指定下标之间的字符 substring(start,end) 开始和结束的位置,从零开始的索引javascript 参数 描述 start 必需.一个非负 ...
- 2018-10-04 [日常]用Python读取word文档中的表格并比较
最近想对某些word文档(docx)的表格内容作比较, 于是找了一下相关工具. 参考Automate the Boring Stuff with Python中的word部分, 试用了python-d ...
- Dynamics 365-部分用户访问环境缓慢
链接来自MS MVP 罗勇大神的Dynamics 365中部分账号使用系统明显缓慢怎么办?先这么干! 之前项目中也遇到过客户部分账户访问环境缓慢的问题,在此做个记录,等再碰到了,以此思路进行尝试
- 一句SQL完成动态分级查询
在最近的活字格项目中使用ActiveReports报表设计器设计一个报表模板时,遇到一个多级分类的难题:需要将某个部门所有销售及下属部门的销售金额汇总,因为下属级别的层次不确定,所以靠拼接子查询的方式 ...
- Android--手势及触摸事件的注意点(一)
实现onInterceptTouchEvent方法可以用来拦截父ViewGroup传递下来的所有触屏事件,可以将所有触屏事件交由此ViewGroup自身的onTouchEvent来处理,也可以继续传递 ...
- (后端)SQL SERVER 字符串按数字排序
应用于B1-1,B1-2,B10-1,B11-1 sqlserver肯定不能按照字符串进行排序,需要进行处理一番: select CONVERT(varchar, LEFT(code,1)),conv ...
- CSS布局---居中方法
在web页面布局中居中是我们常遇到的情况,而居中分为水平居中与垂直居中 文本的居中 CSS中对文本的居中做的非常友好,我们是需要text-align, line-height 两个属性就可以控制文本的 ...
- 使用VSTS的Git进行版本控制(一)——复制现有仓库
使用VSTS的Git进行版本控制(一)--复制现有仓库 概述 Team Services支持两种类型的版本控制Git和Team Foundation Version Control (TFVC).以下 ...
- 关于PID的如何修改的FAQ
1.如何查询支付宝账号对应的PID: 2.意外绑错或者想修改PID流程: (1)和业务联系拿到商家平台支付宝变更协议书填写(模版如下,可在附件中下载) 致:杭州银盒宝成科技有限公司 本人因 ...