装饰器

写在前面

首先理解下,python的装饰器@mydec是一个语法糖,它的本质是func = mydec(func),也就是说把函数func作为参数传入在mydec中。

引子

假设一个函数要打印用户的姓名、年龄、性别当然还有其他Blablabla…

def print_human_information(name, age, sex):
    print("name is {}, age is {}, sex is {}.".format(name, age, sex))
    
if __name__ == "__main__":
    print_human_information(name="Lee", age=-10, sex='unknown')

然后突然你想看下这个函数执行花了多少时间,可能你会这么写:

import time

def print_human_information(name, age, sex):
    start = time.clock()
    
    print("name is {}, age is {}, sex is {}.".format(name, age, sex))
    
    end = time.clock()
    print "execute in {:.2f} ms".format((end - start) * 1000)
    
if __name__ == "__main__":
    print_human_information(name="Lee", age=-10, sex='unknown')

增加一个start,一个end,完美!
但是,如果再有一个函数不打印human,打印animal呢?Ctrl+C、V?太low逼!我们有更好的办法解决这个问题。

import time

def timeused(function):
    def wrapper(name, age, sex):
        start = time.clock()
        
        function(name, age, sex)
        
        end = time.clock()
        print "execute in {:.2f} ms".format((end - start) * 1000)
    return wrapper
        
def print_human_information(name, age, sex):
    print("name is {}, age is {}, sex is {}.".format(name, age, sex))

# 看这里,看这里,看这里
# 重要的事情说三遍
print_human_information = timeused(print_human_information)    
    
if __name__ == "__main__":
    print_human_information(name="Lee", age=-10, sex='unknown')

现在只要有了print_human_information = timeused(print_human_information)main函数中不需要做任何修改就可以显示函数执行的时间,接着让我们再来一个更简洁的语法。

@timeused
def print_human_information(name, age, sex):
    …

完美!

进阶

现在打印animal的函数真的来了,然而参数个数只有两个:

@timeused
def print_animal_information(name, age):
    print("name is {}, age is {}.".format(name, age))

好吧,timeused报错了,修改内嵌函数以及function的参数形式如下,支持任意多个参数:

def timeused(function):
    def wrapper(*args, **kwargs):
        start = time.clock()

        function(*args, **kwargs)
        
        end = time.clock()
        print "execute in {:.2f} ms".format((end - start) * 1000)
    return wrapper

God祥曾经教导我们:

一旦满足了某个需求,那么接下来类似的需求就会接踵而至。

很快,我们需要一个装饰器来检查print_human_informationprint_animal_informationagesex的合法性。(如年龄不能为负数,人类性别只能是male或者female,而对于动物我们不关心,因此该项不检查)
或许,现在是时候编写一个包含参数的装饰器了!

# 默认不检查年龄和性别
def check(check_age = False, check_sex = False):
    def wrapper(function):
        def _wrapper(*args, **kwargs):
            
            if check_age:
                _age = kwargs['age']
                if _age < 0 or _age > 140:
                    print "invalid age '{}', use default {}.".format(_age, 0)  
                    kwargs['age'] = 0
            
            if check_sex:
                _sex = kwargs['sex']
                if _sex != 'male' or _sex != 'female':
                    print "invalid sex '{}', use default {}.".format(_sex, 'male')
                    kwargs['sex'] = 'male'
                    
            function(*args, **kwargs)
            # 用于展示装饰器嵌套的效果
            time.sleep(0.5)
            
        return _wrapper
    return wrapper

@check(check_age = True, check_sex = True)
@timeused
def print_human_information(name, age, sex):
    …
    
@timeused 
@check(check_age = True)
def print_animal_information(name, age):
  …

资料

Python装饰器学习(九步入门)
PEP 0318 -- Decorators for Functions and Methods
PythonDecoratorLibrary

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 每个人都有的内裤主要功能是用来遮羞,但是到了冬天它没法为我们防风御寒,咋办?我们想到的一个办法就是把内裤改造一下,...
    chen_000阅读 1,370评论 0 3
  • 本文为《爬着学Python》系列第四篇文章。从本篇开始,本专栏在顺序更新的基础上,会有不规则的更新。 在Pytho...
    SyPy阅读 2,514评论 4 11
  • 装饰是为函数和类指定管理代码的一种方式.装饰器本身的形式是处理其他的可调用对象的可调用的对象。 函数装饰器在函数定...
    低吟浅唱1990阅读 235评论 0 0
  • 呵呵!作为一名教python的老师,我发现学生们基本上一开始很难搞定python的装饰器,也许因为装饰器确实很难懂...
    TypingQuietly阅读 19,587评论 26 186
  • 煮毛豆、花生、西红柿牛腩……我不顾形象地把餐桌上一切我能吃的东西往嘴里塞,最后当大家都放下筷子我还恬不知耻地...
    桃子在寻找阅读 433评论 0 2