python装饰器的理解和使用

python装饰器的理解和使用

python装饰器,是啥?简单来说:python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的输入值是函数(内存地址)返回值也是一个函数(内存地址),使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。


先简单的来看一下python装饰器能干吗.

def hello_world():
    print ("hello_world")

一个简单的hello_world函数,现在想在在输出“hello_world”之后再输出一行“hello_world end”表示函数运行结束,在学习装饰器之前肯定是这样写的

def hello_world():
    print("hello_world")
    print"hello_world end"

且不说侵入函数体修改这个弊端,一个函数可以这么修改,但是如果现在有100个函数让你这样修改呢?
那在不知道如何使用装饰器的情况下有什么方法呢?

import inspect
def fun_end():
    fun_name=inspect.stack()[1][3]
    print("%s end"%(fun_name))

def hello_world():
    print ("hello_world")
    fun_end()

def hello_man():
    print ("hello_man")
    fun_end()

hello_world()
hello_man()

添加一个函数,让他反复执行,虽然减少了工作量但是还是没解决问题的本质啊。100个函数我得执行100次,关键还是得侵入函数体修改啊。
装饰器的本质就是一个函数,函数体存放着对应的内存地址中,函数名就相当于这个地址的门牌号。我们知道python中一切皆对象,函数也是对象。有了这些知识储备我们可以尝试自己写个装饰器。

import sys
def deco(func):
    func()
    print("%s end"%(func.__name__))

def hello_world():
    print ("hello_world")

def hello_man():
    print ("hello_man")

deco(hello_world)
deco(hello_man)

这种写法解决了侵入函数体修改代码的问题,但是如果说一个项目组调用100次hello_world这个函数,还得去调用的地方一个一个修改过去,那有没有不改变调用方式,也不在原函数体内修改,从而使函数新增功能的方法呢?答案肯定是有的,如下所示:

import sys
def deco(func):
    def wrapper():
        func()
        print("%s end"%(func.__name__))
        return func
    return wrapper

def hello_world():
    print ("hello_world")

def hello_man():
    print ("hello_man")

hello_world=deco(hello_world)
hello_man=deco(hello_man)

hello_world()
hello_man()

前面已经说了hello_world只是相当于内存中存放函数体的门牌号,hello_world=deco(hello_world)这行代码,第一步将原hello_world的地址作为参数赋给了func,执行func相当于执行原hello_world的函数体。再使得新hellow_world这个门牌号等于deco(hello_world)的return结果,既hello_world这个门牌号等于wrapper(称之为闭包)这个门牌号,执行hello_world相当于执行了wrapper这个地址里的函数体。
通过以上内容验证了装饰器的本质就是函数,再python中支持通过@语法糖来优化装饰器的写法。

import sys
def deco(func):
    def wrapper():
        func()
        print("%s end"%(func.__name__))
        return func
    return wrapper
@deco  #相当于 hello_world=deco(hello_world)
def hello_world():
    print ("hello_world")
@deco
def hello_man():
    print ("hello_man")

hello_world()
hello_man()

理解了装饰器的本质后,如果我们想要装饰的函数带参数怎么办呢?非常简单啊,前面都说了执行hellow_world相当于执行闭包wrapper的内容,原hellow_world函数有参数,给wrapper加上就行了再给赋给func就行了啊,但注意为了能使一个装饰器修饰多个不同的函数,这边不能把参数个数写死啊,所以用*args,和**kwargs。

import sys
def deco(func):
    def wrapper(*args,**kwargs):
        func(*args,**kwargs)
        print("%s end"%(func.func_name))
        return func
    return wrapper
@deco
def hello_world(name):
    print ("%s hello_world"%(name))
@deco
def hello_man(name,str):
    print ("%s %s hello_man"%(name,str))

hello_world("fan ge")
hello_man("fan ge","shuai bao le")

那问题又来了如果我的装饰函数有参数怎么办?其实只要理解了装饰器的本质这个问题也迎刃而解了。如果看了以下代码还是不能理解,打上断点运行一下就都清楚了。

import sys
def deco(abs):
    def wrapper(func):
        def in_wrapper(*args,**kwargs):
            func(*args,**kwargs)
            print("%s %s end"%(func.__name__,abs))
            return func
        return in_wrapper
    return wrapper
@deco("first")   
"""
相当于先执行deco("first"),执行返回结果为wrapper的内存地址,再执行hello_world=wrapper(hello_world),执行返回结果为in_wrapper的内存地址
"""
def hello_world(name):
    print ("%s hello_world"%(name))
@deco("second")
def hello_man(name,str):
    print ("%s %s hello_man"%(name,str))

hello_world("fan ge")
hello_man("fan ge","shuai bao le")

追问:如果我原被修饰函数有返回值怎么办?不多说直接见以下代码

import sys
def deco(abs):
    def wrapper(func):
        def in_wrapper(*args,**kwargs):
            return_var=func(*args,**kwargs)   #令一个变两等于原被修饰函数
            print("%s %s end"%(func.__name__,abs))
            return return_var     #再返回这个变量就行了
        return in_wrapper
    return wrapper
@deco("first")   #相当于先执行
def hello_world(name):
    print ("%s hello_world"%(name))
@deco("second")
def hello_man(name,str):
    print("%s  %s hello_world" % (name,str))
    return "执行完毕"

hello_world("fan ge")
x=hello_man("fan ge","shuai bao le")
print(x)

装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。

你可能感兴趣的:(Python学习)