python基础-闭包、装饰器

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

(一)闭包的概念
  • 内层函数总是返回外层函数的引用。
  • 内层函数以及引用的环境变量的整体称作闭包
  • 在函数内部定义一个函数,并且这个函数用到了外部函数的变量,那么这个函数,以及用到的一些变量称之为闭包。
(二)闭包的实例
# 外部函数中的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):
        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[n]
    res = fbnq2(n - 1) + fbnq2(n - 2)
    known[n] = 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())
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值