Python——装饰器

  这里的装饰器其实就是设计模式中的装饰器模式,有兴趣的话,可以看一下这篇博客:装饰器模式

  使用装饰器,一般是在这种情况下使用:

  以前定义的一个函数,之前能够完成所需的功能,但是现在发生了新需求,于是之前的函数就不能满足要求了,此时,如果直接修改以前的函数,是一个很不推荐的方法,如果能够通过额外的处理,满足新需求,这才是推荐的。

  

情景

  原需求:

  之前有1个函数,都接收一个参数,然后打印出来(这里只是举例子,所以使用一个简单的操作)

def func1(s):
    print("this is func1", s)

func1("hello world")
# this is func1 hello world

  新需求:

  现在需求发生改变了,需要在执行这两个函数时,要打印出执行该函数时的时间戳,有2种方案:

  方案1、直接修改func1和func2,在函数内部直接添加一个打印时间戳的语句:

import time

def func1(s):
    print(time.time())
    print("this is func1", s)

func1("hello world")
# 1539527465.7553966
# this is func1 hello world

  使用这种方法,可以满足新需求,但是不推荐,因为如果每次都有新需求都去修改以前的代码,那么就说明代码的整体架构不合理。

  方案2:加一个外壳(有点装饰器的意思),在调用原函数之前,先进行其他操作

import time

def func1(s):
    print("this is func1", s)

# 意思很好理解,就是不直接调用之前的函数,也不修改之前的函数
# 而是在调用原来的函数之前,进行其他操作
def test(func, s):
    print(time.time()) #先打印时间戳
    func(s)

test(func1, "hello world")
# 1539527924.1535952
# this is func1 hello world

  方案分析:

  方案1能实现功能,但是已经提到了不推荐。

  方案2好像是能实现功能,这里之所以说是好像,这是因为,他等价于在执行函数先执行其他操作,而打印地时间戳,并不是函数执行时的时间戳。类似于下面这个代码意思:

import time

def func1(s):
    print("this is func1", s)

# 等价于在电泳函数之前先进性其他操作,但是这个时间戳并不一定是函数开始执行的时间
print(time.time())
test(func1, "hello world")
# 1539527924.1535952
# this is func1 hello world

  另外,方案2中,定义了一个新的函数,也就是说,要想实现新的功能,那么就必须调用新的函数,只不过将原函数当做参数传入新的函数而已,那么在调用这个函数的地方都要做修改,那还不如直接重新定义一个新的函数呢,然后直接调用新的函数即可,所以,这也是一大问题。

 

 

使用装饰器

  最简陋的装饰器:

import time

def func1(s):
    print("this is func1", s)

# 定义装饰器, 装饰器名称为my_decorator,也可是其他的
def my_decorator(func):
    # 新增的业务逻辑,都是写在wrapper中
    def wrapper(s):
        print(time.time())
        func(s)
    return wrapper

f = my_decorator(func1)
f("hello world")
# 1539529258.4292638
# this is func1 hello world

  上面这个代码,好像和方案2没有什么区别,要说有区别的话,也就是上面这个代码返回的是一个函数,而方案二没有任何返回,是直接在新函数中调用的原函数。

  上面这个代码和方案2有一样的问题:调用的时候,不是使用原函数名,虽然在新的函数内调用的是原函数名,但是在外部调用的却是新函数名,这样是很不好的,需要修改其他调用该函数的地方。

  python语法糖:

import time

# 定义装饰器, 装饰器名称为my_decorator,也可是其他的
def my_decorator(func):
    # 新增的业务逻辑,都是写在wrapper中
    def wrapper(s):
        print(time.time())
        func(s)
    return wrapper

# 在函数名之前加上@装饰器
@my_decorator
def func1(s):
    print("this is func1", s)

# 加了装饰器后,可以通过调用原函数就能满足新需求
func1("hello world")
# 1539529258.4292638
# this is func1 hello world

  这就是python中装饰器的核心,添加新功能后,仍旧可以使用原函数来完成新功能。可以定义时复杂,但不要调用时复杂!

 

 

拓展一:使用可变参数

  现在有这么一个问题:func1执行的时候,需要1个参数;但是现在有一个func2,需要接收两个参数(甚至有func3,接收3个参数),也需要增加同样的需求,那么,需要再为func2定义一个装饰器吗?如果不再定义一个装饰器,那wrapper里面接收的参数,只是1个参数,那怎么让多个函数都使用一个装饰器呢?答案:在wrapper中使用可变参数。

import time

def my_decorator(func):
    # wrapper中不再接收指定个数的参数,而是接收可变参数,格式为*args,可以更改
    def wrapper(*args):
        print(time.time())
        func(*args)
    return wrapper

# 在函数名之前加上@装饰器
@my_decorator
def func1(s):
    print("this is func1", s)

@my_decorator
def func2(s1, s2):
    print("this is func2", s1, s2)


func1("hello world")
# 1539530842.152668
# this is func1 hello world

func2("hello python", "zen of python")
# 1539530842.152668
# this is func2 hello python zen of python

  

 

拓展2:使用关键字参数

 

posted @ 2018-10-14 23:10  寻觅beyond  阅读(222)  评论(0)    收藏  举报
返回顶部