闭包:如果一个内部函数,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(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