04 July 2014

来源

这是在Python学习小组上介绍的内容,现学现卖、多练习是好的学习方式。

第一步:最简单的函数,准备附加额外功能

'''
test1.py

最简单的函数,表示调用了两次
'''
def myfunc1():
	print("myfunc() 1 called.")

myfunc1()
myfunc1()

结果

myfunc() 1 called.
myfunc() 1 called.

第二步:使用装饰函数在函数执行前和执行后分别附加额外功能

'''
test2.py

装饰函数的参数是被装饰的函数对象,返回原函数对象
装饰的实质语句: myfunc = deco(myfunc)
'''

def  deco2(func):
	print("before myfunc() 2 called.")
	func()
	print("after myfunc() 2 called.")
	return  func

def  myfunc2():
	print(" myfunc() 2 called.")

print
myfunc =deco2(myfunc2)
print
myfunc()
print
myfunc()

结果

before myfunc() 2 called.
myfunc() 2 called.
after myfunc() 2 called.

myfunc() 2 called.

myfunc() 2 called.

第三步:使用语法糖@来装饰函数

'''
test3.py

使用语法糖@来装饰函数,相当于“myfunc = deco(myfunc)”
但发现新函数只在第一次被调用,且原函数多调用了一次
'''
def deco3(func):
	print("before myfunc() 3 called.")
	func()
	print("after myfunc() 3 called.")
	return func

@deco3
def myfunc3():
	print("myfunc() 3 called.")

myfunc3()
myfunc3()

结果

before myfunc() 3 called.
myfunc() 3 called.
after myfunc() 3 called.

myfunc() 3 called.

myfunc() 3 called.

第四步:使用内嵌包装函数来确保每次新函数都被调用

'''
test4.py

使用内嵌包装函数来确保每次新函数都被调用,
内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象
'''
def deco4(func):
	def deco():
	    print("before myfunc() 4 called.")
	    func()
	    print(" after myfunc() 4 called.")
	    #不需要返回func,实际上应返回原函数的返回值
	return deco

@deco4
def  myfunc4():
	print(" myfunc() 4 called.")
	return 'ok'

myfunc4()
myfunc4()

结果

before myfunc4() called.
myfunc() 4 called.
after myfunc4() called.

before myfunc4() called.
myfunc() 4 called.
after myfunc4() called.

第五步:对带参数的函数进行装饰

'''
test5.py

对带参数的函数进行装饰,
内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象
'''
def deco5(func):
	def deco(a, b):
	    print("before myfunc() called.")
	    ret =func(a, b)
	    print("  after myfunc() called. result: %s"%ret)
	  	return ret
	return deco

@deco5
def myfunc5(a, b):
	print(" myfunc(%s,%s) called."%(a, b))
	return  a+b

ret1 = myfunc5(1, 2)
print ret1
ret2 = myfunc5(3, 4)
print ret2

结果

before myfunc() called.
myfunc(1,2) called.
after myfunc() called. result: 3
3
before myfunc() called.
myfunc(3,4) called.
after myfunc() called. result: 7
7

第六步:对参数数量不确定的函数进行装饰

'''
test6.py

对参数数量不确定的函数进行装饰,
参数用(*args, **kwargs),自动适应变参和命名参数
'''
def deco6(func):
	def _deco(\*args, \*\*kwargs):
	    print("before %s called."%func.__name__)
	    ret =func(\*args, \*\*kwargs)
	    print("  after %s called. result: %s"%(func.__name__, ret))
	    return ret
	return _deco

@deco6
def myfunc61(a, b):
	print(" myfunc61(%s,%s) called."%(a, b))
	return a+b

@deco6
def myfunc62(a, b, c):
	print(" myfunc62(%s,%s,%s) called."%(a, b, c))
	return a+b+c

myfunc61(1, 2)
print
myfunc61(3, 4)
print
myfunc62(1, 2, 3)
print
myfunc62(3, 4, 5)

结果

before myfunc61 called.
myfunc61(1,2) called.
after myfunc61 called. result: 3

before myfunc61 called.
myfunc61(3,4) called.
after myfunc61 called. result: 7

before myfunc62 called.
myfunc62(1,2,3) called.
after myfunc62 called. result: 6

before myfunc62 called.
myfunc62(3,4,5) called.
after myfunc62 called. result: 12

第七步:让装饰器带参数

'''
test7.py

在 test4\.py 的基础上,让装饰器带参数,
和上一示例相比在外层多了一层包装。
装饰函数名实际上应更有意义些
'''
def deco7(arg):
    def _deco(func):
        def __deco():
            print("before %s called [%s]."%(func.__name__, arg))
            func()
            print("  after %s called [%s]."%(func.__name__, arg))
        return __deco
    return _deco

@deco7("module 1")
def myfunc71():
    print(" myfunc71() called.")

@deco7("module 2")
def myfunc72():
    print(" myfunc72() called.")

myfunc71()
print
myfunc72()

结果

before myfunc71 called [module 1].
 	myfunc71() called.
  	after myfunc71 called [module 1].

before myfunc72 called [module 2].
 	myfunc72() called.
  	after myfunc72 called [module 2].

第八步:让装饰器带类参数

'''
test8.py
装饰器带类参数
'''
class  locker:
    def  __init__(self):
    print("locker.__init__() should be not called.")

    @staticmethod
    def acquire():
        print("locker.acquire() called.(这是静态方法)")

    @staticmethod
    def release():
        print("  locker.release() called.(不需要对象实例)")

    def deco8(cls):
    '''cls 必须实现acquire 和 release 静态方法'''
        def _deco(func):
            def __deco():
                print("before %s called [%s]."%(func.__name__, cls))
                cls.acquire()
                try:
                    return func()
                finally:
                    cls.release()
            return  __deco
        return _deco

@deco8(locker)
def myfunc8():
    print(" myfunc() called.")

myfunc8()
myfunc8()