闭包、装饰器

闭包:如果一个内部函数,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。

  闭包 = 内部函数 + 定义函数时的环境

 1 def outer(x):
 2     def inner():  # 条件1 inner就是内部函数
 3         print(x)  # 条件2 外部环境的一个变量
 4     
 5     return inner  # 结论:内部函数inner就是一个闭包
 6 
 7 
 8 # inner()  # 1 局部变量,全局无法调用
 9 
10 f = outer(8)  # 闭包就是一种能够调用内部函数的现象。
11 f()

开放封闭原则:对修改封闭,对扩展开放。

函数装饰器:

  无参函数:

 1 import time
 2 
 3 def show_time(f):
 4     def inner():
 5         start = time.time()
 6         f()
 7         end = time.time()
 8         print('spend %s' % (end - start))
 9     return inner
10 
11 @show_time  # foo=show_time(foo)
12 def foo():
13     print('foo....')
14     time.sleep(2)
15 
16 foo()

   有参函数:

 1 import time
 2 
 3 def show_time(f):
 4     def inner(x, y):
 5         start = time.time()
 6         f(x, y)  # 执行原函数(foo)
 7         end = time.time()
 8         print('spend %s' % (end - start))
 9     return inner
10 
11 @show_time  # foo=show_time(foo)
12 def foo(a, b):
13     print(a + b)
14     time.sleep(2)
15 
16 # 之前调用foo时执行foo里的代码,加上装饰器后调用foo会执行inner中的代码,inner中的f就是foo,参数要一一对应。
17 foo(3, 5)

  有参装饰器:

 1 import time
 2 
 3 def logger(flag=False):
 4     def show_time(f):
 5         def inner(x, y):
 6             start = time.time()
 7             f(x, y)  # 执行原函数(foo)
 8             end = time.time()
 9             print('spend %s' % (end - start))
10 
11             if flag:
12                 print('打印日志')
13         return inner
14     return show_time
15 
16 @logger(True)  # 相当于@show_time,与上例的区别仅在于多了一个参数
17 def foo(a, b):
18     print(a + b)
19     time.sleep(2)
20 
21 # 之前调用foo时执行foo里的代码,加上装饰器后调用foo会执行inner中的代码,inner中的f就是foo,参数要一一对应。
22 foo(3, 5)

  嵌套装饰器:

 1 def makebold(fn):
 2     print('bold')
 3     def wrapper2():
 4         return "<b>" + fn() + "</b>"    # fn=wapper1
 5         # print("<b>" + fn() + "</b>")  # fn=wapper1
 6     return wrapper2
 7 
 8 def makeitalic(fn):
 9     print('italic')
10     def wrapper1():
11         return "<i>" + fn() + "</i>"  # fn=hello
12         #  这里必须使用return,不能使用print,因为print("<b>" + None + "</b>")报错
13         # print("<i>" + fn() + "</i>")
14     return wrapper1
15 
16 @makebold  # wapper1=makebold(wapper1) --> wapper2  步骤2
17 @makeitalic  # hello=makeitalic(hello) --> wapper1  步骤1
18 def hello():
19     return "hello decorater"
20 
21 s = hello()
22 print(s)
23 # italic
24 # bold
25 # <b><i>hello decorater</i></b>
26 
27 # print("<b>" + None + "</b>")  # 报错

类装饰器:

 1 import time
 2 
 3 class Foo(object):
 4     def __init__(self, func):
 5         self._func = func
 6 
 7     def __call__(self):
 8         start_time = time.time()
 9         self._func()
10         end_time = time.time()
11         print('spend %s' % (end_time-start_time))
12 
13 @Foo  #bar=Foo(bar)
14 def bar():
15     print('bar')
16     time.sleep(2)
17 
18 bar()  # 调用__call__方法

  使用装饰器极大地复用了代码,但有一个缺点就是原函数的元信息丢失了,比如docstring、__name__,参数列表等。

 1 def foo():
 2     print("hello foo")
 3 
 4 print(foo.__name__)
 5 #####################
 6 
 7 def logged(func):
 8     def wrapper(*args, **kwargs):
 9         print(func.__name__ + " was called")
10         return func(*args, **kwargs)
11     return wrapper
12 
13 @logged  # cal=logged(cal) --> wapper
14 def cal(x):
15    return x + x * x
16 
17 print(cal.__name__)
18 ########
19 # foo
20 # wrapper  # 原函数信息丢失

  补救措施:

    functools.waps,把原函数的元信息拷贝到装饰器函数中。

 1 from functools import wraps
 2 
 3 def logged(func):
 4     @wraps(func)
 5     def wrapper(*args, **kwargs):
 6         print(func.__name__ + " was called")
 7         return func(*args, **kwargs)
 8     return wrapper
 9 
10 @logged  # cal=logged(cal) --> wapper
11 def cal(x):
12    return x + x * x
13 
14 print(cal.__name__)
15 # cal

参考:http://www.cnblogs.com/yuanchenqi/articles/5830025.html

原文地址:https://www.cnblogs.com/yangxiaoling/p/6901581.html