什么是闭包?

官方解释:

在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自有变量的函数。
这个被引用的自有变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外

装饰器

装饰器就是修改其他函数功能的函数。使用装饰器可以简化代码,让你的代码看起来更加 pythonic。

python 提供了 @ 符号作为装饰器的语法糖。使用语法糖要求装饰函数必须 return 一个函数对象

1. 对不带参数的函数进行装饰

def logging(func):

	def _deco():
		print("%s is running"% func.__name__)
		func()

	return _deco

@logging
def bar():
	print('I am bar')

bar()

# 运行结果
#bar is running
#I am bar

2. 对带参数的函数进行装饰

def logging(func):

	def _deco(a, b):
		print("%s is running"% func.__name__)
		func(a, b)

	return _deco

@logging
def bar(a, b):
	print('I am bar:', a+b)

bar(1, 2)

# 运行结果
#bar is running
#I am bar:3

3. 函数参数数量可变

def logging(func):

	def _deco(*args, **kwargs):
		print("%s is running"% func.__name__)
		func(*args, **kwargs)

	return _deco

@logging
def bar(a, b):
	print('I am bar:', a+b)


@logging
def foo(a, b, c):
	print('I am foo:', a+b+c)

@logging
def foo2(**kwargs):
	for k in kwargs:
		print('I am foo2:', kwargs[k])

bar(1, 2)
foo(1, 2, 3)
foo2(a=1)

# 运行结果
# bar is running
# I am bar: 3
# foo is running
# I am foo: 6
# foo2 is running
# I am foo2: 1

4. 装饰器带参数

带参数的装饰器函数将有 3 层函数,如下所示:


def logging(level):

	def _deco(func):
		def __deco(*args, **kwargs):
			if level == 'error':
				print("%s is running"% func.__name__)
			return func(*args, **kwargs)

		return __deco
	return _deco

@logging('error')
def bar(a, b):
	print('I am bar:', a+b)

# 运行结果
# bar is running
# I am bar: 3
# I am foo: 6

5. python 自带的装饰器函数 functools.wraps(func)

使用 @functools.wraps(func) 装饰器,可以返回原函数的元信息,比如 docstring、name、参数列表等

如下未使用该装饰器,打印 bar.name:

使用了该装饰器:

6. 实现带参数 和 不带参数的装饰器的自适应

import functools


def logging(arg):
	if callable(arg):  #如果传入的参数是个函数,那么走不带参数的装饰器函数分支
		@functools.wraps(arg)
		def _deco(*args, **kwargs):
			print("%s is running" % arg.__name__)
			return arg(*args, **kwargs)
		return _deco

	else: # 否则走带参数的装饰器函数分支
		def _deco(func):
			@functools.wraps(func)
			def __deco(*args, **kwargs):
				if arg == 'error':
					print("error: %s is running"% func.__name__)
				return func(*args, **kwargs)

			return __deco
		return _deco


@logging('error')
def bar(a, b):
	print('I am bar:', a+b)
	print(bar.__name__)


@logging
def foo(a, b, c):
	print('I am foo:', a+b+c)

bar(1, 2)
foo(1, 2, 3)

# 结果:
#error: bar is running
#I am bar: 3
#bar
#foo is running
#I am foo: 6

7. 装饰器类

上面介绍的都是装饰器函数,但在使用时有时只想打日志到一个文件,有时要把引起问题的异常发送到一个邮件,同时也保留日志。这个时候就需要用到类装饰器、以及继承来实现。

运行结果:

12-20 04:49