美文网首页
闭包和装饰器

闭包和装饰器

作者: MononokeHime | 来源:发表于2018-09-04 21:54 被阅读0次

闭包

之前一直不明白闭包的定义-内层函数引用了外层函数的变量(包括它的参数)就构成了闭包。我觉得也没什么了不起的,干嘛还专门起一个别扭的名字闭包呢。后来发现,其实并不是想的这么简单,内层函数调用外层变量的时候,这时候外层函数已经调用结束返回了,理论上外层函数所有的局部变量都已经释放,所以内层函数不应该拿到外层函数的变量(包括它的参数)。闭包就是将使用到的变量(称为环境变量)绑定到内层函数的__closure__属性上。

image.png
def make_averager():
    series = []

    def averager(new_value):
        series.append(new_value)
        total = sum(series)
        return total/len(series)
    return averager

avg = make_averager()
print(avg.__code__.co_freevars[0])  # series
print(avg.__closure__[0].cell_contents)  # []

avg(10)
avg(12)
print(avg.__code__.co_freevars[0])  # series
print(avg.__closure__[0].cell_contents) # [10, 12]

装饰器

装饰器就是在不改变原函数的调用方式的情况下,在函数的前后添加功能。装饰器完美的展示了开放封闭原则,即对扩展开放,对修改封闭。

关于装饰器的一些概念

  • 普通装饰器:@decorator
  • 装饰器工厂函数:本身不是装饰器,返回的是装饰器 @decoratorfunc(args) <=>@decorator
  • 参数化装饰器:通过装饰器工厂函数的方式,将参数转给装饰器,典型的就是Flask的路由
  • 类装饰器
  • 叠放装饰器

普通装饰器

装饰器函数decorator定义的时候参数必须是一个函数。
普通装饰器使用@decorator语法糖装饰函数时是不带参数的,它会默认将被装饰的函数作为它的参数进行传递

def decorator(func):
    def inner(*args,**kwargs):
        "在被装饰函数之前要做的事"
        ret = func(*args,**kwargs)
        "在被装饰函数之后要做的事"
        return ret
    return inner

@decorator
def myfunc(*args,**kwargs):
    print('im myfunc')

解释器会将@decorator装饰过程解释成下面这样的语句:

myfunc = decorator(myfunc)  # 此时myfunc->inner

也就是说就算我没有调用myfunc函数,就已经首先调用了decorator函数了。

带参数的装饰器

当我们看到@decoratorfunc(arg1,arg2)这种带有参数的装饰器的时候,我们首先意识到decoratorfunc一定不是装饰器函数,而是装饰器工厂函数,它返回的是装饰器。这样子通过装饰器工厂函数将参数传递给内部进行处理。通常带参数的装饰器的定义会是三层函数嵌套,第一层返回装饰器,第二层返回被装饰函数,第三层根据需求返回或者不返回。

叠放装饰器

@decorator_one
@decorator_two
def func():
    pass

相当于:

func = decorator_one(decorator_two(func))

类装饰器

类装饰器一般都实现了__call__方法,请看下例

# 定义一个Time类
class Time(object):
    def __init__(self,func): # 注意这里要设定参数接收Test
        self._func = func
    def __call__(self):
        print('我可以对函数进行装饰')
        self._func()

@Time # 等价于 Test = Time(Test),此时Test是一个实例对象
def Test():
    print('测试一下')

Test()  # 调用__call__方法

#结果为:
#我可以对函数进行装饰
#测试一下

装饰器引发的问题

使用装饰器会覆盖被装饰函数的一些属性,例如 __name____doc__,因此可以使用@warps(func)装饰器来恢复被装饰函数myfunc的相关属性

from functiontools import wraps
def decorator(func):
    @wraps(func)
    def inner(*args,**kwargs):
        "在被装饰函数之前要做的事"
        ret = func(*args,**kwargs)
        "在被装饰函数之后要做的事"
        return ret
    return inner

@decorator
def myfunc(*args,**kwargs):
    print('im myfunc')

print(myfunc__name__)  # myfunc

装饰器的案例

1.注册回调函数

下面这个示例展示了通过URL的路由来调用相关注册的函数示例:

class MyApp():
    def __init__(self):
        self.func_map = {}
 
    def register(self, name):
        def func_wrapper(func):
            self.func_map[name] = func
            return func
        return func_wrapper
 
    def call_method(self, name=None):
        func = self.func_map.get(name, None)
        if func is None:
            raise Exception("No function registered against - " + str(name))
        return func()
 
app = MyApp()
 
@app.register('/')
def main_page_func():
    return "This is the main page."
 
@app.register('/next_page')
def next_page_func():
    return "This is the next page."
 
print app.call_method('/')
print app.call_method('/next_page')

注意:
1)上面这个示例中,用类的实例来做decorator。
2)decorator类中没有__call__(),但是wrapper返回了原函数。所以,原函数没有发生任何变化。

2.线程异步

下面量个非常简单的异步执行的decorator,注意,异步处理并不简单,下面只是一个示例。

from threading import Thread
from functools import wraps
 
def async(func):
    @wraps(func)
    def async_func(*args, **kwargs):
        func_hl = Thread(target = func, args = args, kwargs = kwargs)
        func_hl.start()
        return func_hl
 
    return async_func
 
if __name__ == '__main__':
    from time import sleep
 
    @async
    def print_somedata():
        print 'starting print_somedata'
        sleep(2)
        print 'print_somedata: 2 sec passed'
        sleep(2)
        print 'print_somedata: 2 sec passed'
        sleep(2)
        print 'finished print_somedata'
 
    def main():
        print_somedata()
        print 'back in main'
        print_somedata()
        print 'back in main'
 
    main()

3.给函数调用做缓存

from functools import wraps
def memo(fn):
    cache = {}
    miss = object()
 
    @wraps(fn)
    def wrapper(*args):
        result = cache.get(args, miss)
        if result is miss:
            result = fn(*args)
            cache[args] = result
        return result
 
    return wrapper
 
@memo
def fib(n):
    if n < 2:
        return n
    return fib(n - 1) + fib(n - 2)

上面这个例子中,是一个斐波拉契数例的递归算法。我们知道,这个递归是相当没有效率的,因为会重复调用。比如:我们要计算fib(5),于是其分解成fib(4) + fib(3),而fib(4)分解成fib(3)+fib(2),fib(3)又分解成fib(2)+fib(1)…… 你可看到,基本上来说,fib(3), fib(2), fib(1)在整个递归过程中被调用了两次。

而我们用decorator,在调用函数前查询一下缓存,如果没有才调用了,有了就从缓存中返回值。一下子,这个递归从二叉树式的递归成了线性的递归。

相关文章

  • python 高级 13闭包 装饰器

    闭包和装饰器 1.8 闭包和装饰器 学习目标 1. 能够说出闭包的定义形式 2. 能够说出装饰器的实现形式 ...

  • Python装饰器-专题笔记

    学会装饰器,Python更进阶 函数作用域到闭包到装饰器讲解,及闭包和装饰器的运用。 [√] 慕课网Meshare...

  • Python的自定义超时机制——装饰器的妙用

    装饰器 关于装饰器的入门,可以参考这篇文章:12步轻松搞定python装饰器简单来说,装饰器其实就是一个闭包(闭包...

  • python装饰器

    学习了闭包的概念之后,再来学习装饰器就简单很多。装饰器就是闭包的一个应用 代码举例 但是装饰器在使用时分加载态和调...

  • Python简明教程第15节:装饰器参数和装饰器解除

    装饰器参数 上面介绍的装饰器中的闭包和被装饰的函数的参数是相同的。 其实只要保证闭包和被装饰的函数中的参数保持一致...

  • Python-闭包和修饰器

    作用域 闭包 code: 装饰器 code: 装饰器案例 code:

  • 闭包和装饰器

    一、闭包 什么是闭包?在了解这个概念之前,我们先来看一个小例子。 上面的例子中,我们看到有一个外部函数,内部定义了...

  • 闭包和装饰器

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

  • 闭包和装饰器

    闭包 之前一直不明白闭包的定义-内层函数引用了外层函数的变量(包括它的参数)就构成了闭包。我觉得也没什么了不起的,...

  • 闭包和装饰器

    什么是闭包? 各种专业文献的闭包定义都非常抽象,我的理解是:闭包就是能够读取其他函数内部变量的函数。 """ 使用...

网友评论

      本文标题:闭包和装饰器

      本文链接:https://www.haomeiwen.com/subject/pnxnwftx.html