您的位置:首页 > 编程语言 > Python开发

python基础-闭包、装饰器

2018-02-27 15:51 357 查看

一、闭包(在闭包中调用需要装饰的函数,就成了装饰器)

(一)闭包的概念

内层函数总是返回外层函数的引用。

内层函数以及引用的环境变量的整体称作闭包

在函数内部定义一个函数,并且这个函数用到了外部函数的变量,那么这个函数,以及用到的一些变量称之为闭包。

(二)闭包的实例

# 外部函数中的a和b称之为环境变量或者自由变量
def line_conf(a, b):
def line(x):
return a * x + b
return line
line1 = line_conf(1, 1)
line2 = line_conf(10, 1)
print(line1(10))
print(line2(10))


(三)在闭包修改环境变量

在python3中实现修改环境变量。

def line_conf(a):
# a和b都可以修改
b = 1
def line(x):
# 在闭包中,修改外部函数的变量或者参数,应该用nonlocal(基本用法和global一样)
nonlocal a, b
b = 10
a = 10
return a * x + b
return line

line1 = line_conf(1)
line2 = line_conf(10)
print(line1(10))
print(line2(10))


在闭包中查看环境变量。

闭包使用的环境变量存储在闭包函数的属性 — 元组对象closure属性中 比如需要访问第0个环境变量的值时 closure[0].cell_contents。

def line_conf(a):
b = 1000
def line(x):
return a * x + b
return line
line1 = line_conf(1)
line2 = line_conf(10)
# 查看环境变量地址,是由元组组成。必须调用闭包引用才能查看
print(line1.__closure__)
# 如果想取环境变量b的值,应该用__closure__[0].cell_contents
print(line1.__closure__[1].cell_contents)


在python2中修改环境变量的值。

python2的出发点就是 元组不可变 但是 元组中成员的引用可以变。所以将环境变量数据打包到一个列表,这个列表在元组是可以变的。

二、装饰器 (添加额外功能,不改变原有函数代码)

(一)装饰器的功能

1、引入日志

2、函数执行的时间统计

3、执行函数预备处理

4、执行函数后清理功能

5、权限校验等场景

6、缓存

(二)多重装饰器

def makebold(func):
def wrapped():
return "<b>" + func() + "</b>"
return wrapped
def makeitalic(func):
def wrapped():
return "<i>" + func() + "</i>"
return wrapped
# 当执行到第一个魔法糖的时候,第一个魔法堂装饰的是 text =  makebold(wrapped) 这个wrapped是makeitalic的
@makebold
# 当执行到第二个魔法糖的时候,第二个魔法糖装饰的是 wrapped = makeitalic(text)
@makeitalic
def text():
return "hello world"
# <b><i>hello world</i></b> 执行顺序是从上往下,但是运行结果的效果是从下往上,穿衣服的过程!!!
print(text())


(三)通用装饰器(无参,被装饰函数带参数,被装饰函数有返回值,装饰器工厂)

import time
# 装饰器工厂,我们可以通过装饰器的参数,实现不同的功能,这样需要对装饰器传递参数
def gettime(arg=None):
def wrapped(func):
# 不定长参数,因为在装饰很多函数时,不知道到底有多少参数
def inner(*args, **kwargs):
start = time.time()
# 有些函数会有返回值,有些没有,那么我们在这也给一个返回值取接收。
ret = func(*args, **kwargs)
end = time.retime()
tm = end - start
if arg:
print(tm)
else:
print(int(tm))
return ret
return inner
return wrapped
@gettime()
def run(a):
for i in range(a):
pass
run(10000000)


(四)类装饰器

装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。在Python中一般callable对象都是函数,但也有例外。只要某个对象重写了 call() 方法,那么这个对象就是callable的。

class Func(object):
# 首先我们要初始化函数,来作为类对象的参数
def __init__(self, func):
self.func = func
# 我们在实力对象的时候必须可调用,需要用call方法实现调用函数
def __call__(self, *args, **kwargs):

c3ea
return self.func()
@Func
def func1():
return "用类装饰器来装饰我"
print(func1())


三、装饰器路由(在Flask中使用)

route_list = [
# ("/gettime.py", gettime),
# ("/aaa.py", aaa)
]
# 如果要想加额外的功能去添加路径和函数到到rout_list中
def route(url):
def func1(func):
# 注意在这添加的是一个元组
# 在这里我们修改了列表(全局变量),所以用global(实际上可以不用global),列表是可变数据类型。
# 问题是如果只用装饰器,那么url没有办法提供,那么我们该添加装饰器的功能,用装饰器工厂
route_list.append((url, func))
def func2(*args, **kwargs):
return func(*args, **kwargs)
return func2
return func1
@route("/gettime.py")
def gettime():
return time.ctime().encode()


四、装饰器-functools

from functools import wraps
def makebold(func):
@wraps(func)    # 这样的话,被装饰的func的函数名位原名,而不是wrapped()
def wrapped():
return "<b>" + func() + "</b>"
return wrapped


五、斐波那契实现

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

def fbnq(n):
assert(n >= 0), u'n必须大于等于0'
return n if n in (0, 1) else fbnq(n - 1) + fbnq(n - 2)

if __name__ == '__main__':
from timeit import Timer
t = Timer('fbnq(8)', 'from __main__ import fbnq')
print(t.timeit())    # 12.6881980896


使用memoization方法看看能否改善。

known = {0:0, 1:1}
def fbnq2(n):
assert (n >= 0), 'n must be >= 0'
if n in known:
return known

res = fbnq2(n - 1) + fbnq2(n - 2)
known
= res
return res
if __name__ == '__main__':
from timeit import Timer
t = Timer('fbnq2(100)', 'from __main__ import fbnq2')
print(t.timeit())  # 0.228129148483


实际我们可以把memoization方法写作一个装饰器,可以应用于很多地方。

from functools import wraps

def memoization(func):
known = dict()
@wraps(func)
def wrapper(*args):
if args not in known:
known[args] = func(*args)
return known[args]
return wrapper

@memoization   # 装饰器
def fbnq(n):
assert(n >= 0), u'n必须大于等于0'
return n if n in (0, 1) else fbnq(n - 1) + fbnq(n - 2)

if __name__ == '__main__':
from timeit import Timer
t = Timer('fbnq(8)', 'from __main__ import fbnq')
print(t.timeit())
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息