Python高级 -- 11 闭包、装饰器

一、闭包


1、什么是闭包


# 定义一个函数
def test(number):

    """
		在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,
		同时,外部函数的返回值是内部函数的引用
		那么将这个函数(内部定义的那个函数)以及用到的一些变量称之为闭包
    """
    def test_in(number_in):
        print("in test_in 函数, number_in is %d" % number_in)
        return number + number_in
    # 其实这里返回的就是闭包的结果
    return test_in


# 给test函数赋值,这个20就是给参数number
ret = test(20)

# 注意这里的100其实是给test函数内部定义的test_in函数的参数number_in赋值
# ret是test函数的返回值,即返回的是test函数内部的定义的test_in函数的引用
print(ret(100))

#注 意这里的200其实给参数number_in
print(ret(200))


二、装饰器


1、先理解下面代码


#### 第一波 ####
def foo():
    print('foo')

foo  # 表示是函数的引用
foo()  # 表示执行foo函数

#### 第二波 ####
def foo():
    print('foo')

foo = lambda x: x + 1

foo()  # 执行lambda表达式,而不再是原来的foo函数,因为foo这个名字被重新指向了另外一个匿名函数
# 以上代码是将lambda表达式赋值给了foo变量,此时,执行foo(),代表的是执行lambda表达式


        函数名仅仅是个变量,只不过指向了定义的函数而已,所以才能通过 函数名()调用;如果 变量被修改了,即函数名=xxx ,那么当在执行 函数名() 时,调用的就不是之前的那个函数了,而是被赋值之后的函数


2、装饰器


使用闭包引入装饰器概念


def validation(fun):

	# 定义一个内部函数
	def doValidation():
		# 执行前置校验
		print("放在 fun() 之前的内容是属于前置校验")

		# 执行fun函数
		fun()

		# 执行后置校验
		print("放在 fun() 之后的内容是属于后置校验")

	# 外部函数返回值是内部函数的引用
	return doValidation


def test():
	print("test方法中加入校验")


"""
	实例化外部函数,参数是其他函数(test函数)的引用
	由于外部函数(validation)中还有一个函数定义,
	因此,实例化外部函数的时候,会定义一个内部函数,返回的是内部函数的引用
	而由于,在内部函数中使用其他函数(test函数)的引用来实例化了一个其他函数(test函数)对象,
	因此,实例化内部函数的时候,会进行其他函数(test函数)的实例化操作
	
"""
test = validation(test)	# 此时的test是内部函数的引用

"""
	执行内部函数,由于内部函数中其他函数(test)的实例化操作
	因此,在内部函数中所执行的代码(本例中为打印操作)可以放在其他函数(test)前或者后进行操作
	即:达到了在执行其他函数前(或者后)进行校验的目的
"""
test()

"""
	运行结果:
		放在 fun() 之前的内容是属于前置校验
		test方法中加入校验
		放在 fun() 之后的内容是属于后置校验
"""


使用装饰器来进行上述操作的代码:


def validation(fun):

	# 定义一个内部函数
	def doValidation():
		# 执行前置校验
		print("放在 fun() 之前的内容是属于前置校验")

		# 执行fun函数
		fun()

		# 执行后置校验
		print("放在 fun() 之后的内容是属于后置校验")

	# 外部函数返回值是内部函数的引用
	return doValidation

@validation
def test():
	print("test方法中加入校验")


"""
	实例化外部函数,参数是其他函数(test函数)的引用
	由于外部函数(validation)中还有一个函数定义,
	因此,实例化外部函数的时候,会定义一个内部函数,返回的是内部函数的引用
	而由于,在内部函数中使用其他函数(test函数)的引用来实例化了一个其他函数(test函数)对象,
	因此,实例化内部函数的时候,会进行其他函数(test函数)的实例化操作
	

	⭐⭐⭐在其他函数上使用 @外部函数名 ,这种方式可以替代以下代码来进行test方法执行前后的校验
	test = validation(test)		# 返回值是内部函数的引用

"""


"""
	执行内部函数,由于内部函数中其他函数(test)的实例化操作
	因此,在内部函数中所执行的代码(本例中为打印操作)可以放在其他函数(test)前或者后进行操作
	即:达到了在执行其他函数前(或者后)进行校验的目的
"""
test()

"""
	运行结果:
		放在 fun() 之前的内容是属于前置校验
		test方法中加入校验
		放在 fun() 之后的内容是属于后置校验
"""


解释:在方法上加上 @其他方法名 的方式即为装饰器的使用,而其他方法是一个包含了内部函数的一个函数,在内部函数中有方法的实例化操作,这种方式就达到了装饰器的效果


3、对带有参数的方法进行装饰器


不使用装饰器的写法:


def validation(fun):

	# 定义一个内部函数
	def doValidation(args):
		# 执行前置校验
		print("放在 fun() 之前的内容是属于前置校验")

		# 执行fun函数
		fun(args)

		# 执行后置校验
		print("放在 fun() 之后的内容是属于后置校验")

	# 外部函数返回值是内部函数的引用
	return doValidation


def test(args):
	print("test方法中加入校验 %s" % args)


test = validation(test)	# 返回值是是内部函数的引用


test(100)


使用装饰器的写法:


def validation(fun):

	# 定义一个内部函数
	def doValidation(args):
		# 执行前置校验
		print("放在 fun() 之前的内容是属于前置校验")

		# 执行fun函数
		fun(args)

		# 执行后置校验
		print("放在 fun() 之后的内容是属于后置校验")

	# 外部函数返回值是内部函数的引用
	return doValidation

@validation
def test(args):
	print("test方法中加入校验 %s" % args)


# test = validation(test)	# 返回值是是内部函数的引用


test(100)


4、不定长参数的函数装饰器


def validation(fun):

	# 定义一个内部函数
	def doValidation(*args, **kwargs):
		# 执行前置校验
		print("放在 fun() 之前的内容是属于前置校验")

		# 执行fun函数
		fun(*args, **kwargs)

		# 执行后置校验
		print("放在 fun() 之后的内容是属于后置校验")

	# 外部函数返回值是内部函数的引用
	return doValidation

@validation  	# test = validation(test)	 返回值是是内部函数的引用
def test(num, *args, **kwargs):
	print("test方法中参数1 %s" % num)
	print("test方法中参数2 : " , args)
	print("test方法中参数3 : " , kwargs)


test(100)
print("-" * 50)
test(100, 200)
print("-" * 50)
test(100, 200, 300, name='李四')


"""
	运行结果:

		放在 fun() 之前的内容是属于前置校验
		test方法中参数1 100
		test方法中参数2 :  ()
		test方法中参数3 :  {}
		放在 fun() 之后的内容是属于后置校验
		--------------------------------------------------
		放在 fun() 之前的内容是属于前置校验
		test方法中参数1 100
		test方法中参数2 :  (200,)
		test方法中参数3 :  {}
		放在 fun() 之后的内容是属于后置校验
		--------------------------------------------------
		放在 fun() 之前的内容是属于前置校验
		test方法中参数1 100
		test方法中参数2 :  (200, 300)
		test方法中参数3 :  {'name': '李四'}
		放在 fun() 之后的内容是属于后置校验

"""


5、带有返回值的函数的装饰器


带有返回值的函数的装饰器亦是通用装饰器(可变参数,有返回值的装饰器)的写法:


def validation(fun):

	# 定义一个内部函数
	def doValidation(*args, **kwargs):
		# 执行前置校验
		print("放在 fun() 之前的内容是属于前置校验")

		# 执行fun函数
		return fun(*args, **kwargs)

		# 执行后置校验
		print("放在 fun() 之后的内容是属于后置校验")

	# 外部函数返回值是内部函数的引用
	return doValidation

@validation  	# test = validation(test)	 返回值是是内部函数的引用
def test(num, *args, **kwargs):
	print("test方法中参数1 %s" % num)
	print("test方法中参数2 : " , args)
	print("test方法中参数3 : " , kwargs)
	return "OK"


ret = test(100)
print(ret)
print("-" * 50)

ret = test(100, 200)
print(ret)
print("-" * 50)

ret = test(100, 200, 300, name='李四')
print(ret)

"""
	运行结果:

		放在 fun() 之前的内容是属于前置校验
		test方法中参数1 100
		test方法中参数2 :  ()
		test方法中参数3 :  {}
		OK
		--------------------------------------------------
		放在 fun() 之前的内容是属于前置校验
		test方法中参数1 100
		test方法中参数2 :  (200,)
		test方法中参数3 :  {}
		OK
		--------------------------------------------------
		放在 fun() 之前的内容是属于前置校验
		test方法中参数1 100
		test方法中参数2 :  (200, 300)
		test方法中参数3 :  {'name': '李四'}
		OK

"""


6、多个装饰器对一个函数进行装饰


def zhuangshi_1(fun):

	print("定义装饰1")
	# 定义一个内部函数
	def neibu_1(*args, **kwargs):
		# 执行前置校验
		print("执行 zhuangshi_1 中的装饰内容")

		# 执行fun函数
		return fun(*args, **kwargs)

	# 外部函数返回值是内部函数的引用
	return neibu_1


def zhuangshi_2(fun):

	print("定义装饰2")
	# 定义一个内部函数
	def neibu_2(*args, **kwargs):
		# 执行前置校验
		print("执行 zhuangshi_2 中的装饰内容")

		# 执行fun函数
		return fun(*args, **kwargs)

	# 外部函数返回值是内部函数的引用
	return neibu_2


@zhuangshi_1
@zhuangshi_2
def test():
	pass

test()

"""
	运行结果:
		定义装饰2
		定义装饰1
		执行 zhuangshi_1 中的装饰内容
		执行 zhuangshi_2 中的装饰内容

	解释:
		定义装饰器的时候,先执行离方法最近的装饰器的定义
		执行装饰器的时候,先执行最上层的装饰器

"""


7、多个装饰器装饰一个函数的应用


def html_1(func):
	def td():
		return "<td>" + func() +"</td>"
	return td


def html_2(func):
	def p1():
		return "<p1>" + func() + "</p1>"
	return p1


@html_1
@html_2
def test():
	return "hello world"

print(test())

	
"""
	运行结果:

		<td><p1>hello world</p1></td>
"""


8、带有参数的装饰器


def parent(arg):
	def validation(func):
		def call_func(*args, **kwargs):
			if arg == 1:
				print("level 1")
			elif arg == 2:
				print("level 2")
			return func()
		return call_func
	return validation

"""
	⭐⭐⭐:带有参数的装饰器调用的时候:
		首先会将参数当作实参,传递到装饰器中,进行第一层函数的调用,并返回第二层函数的引用;
		然后,将第二层函数的引用当作真正的装饰器,进行装饰


		以下调用中,首先@parent(1)会先执行parent方法,把参数1传递进去,返回的是validation的引用
		然后,使用validation作为真正的装饰器,对test1方法进行装饰

"""

@parent(1)
def test1():
	return "OK"
test1()


@parent(2)
def test2():
	return "OK"
test2()


9、用类对函数进行装饰


class Test(object):

	def __init__(self, func):
		self.func = func

	def __call__(self):
		print("对函数进行装饰的方法")
		return self.func()


@Test
def demo():
	return "呵呵"

print(demo())


猜你喜欢

转载自blog.csdn.net/wingzhezhe/article/details/79614825