Python语言之Python装饰器
小标 2018-08-08 来源 : 阅读 1099 评论 0

摘要:本文主要向大家介绍了Python语言之Python装饰器,通过具体的内容向大家展示,希望对大家学习Python语言有所帮助。

本文主要向大家介绍了Python语言之Python装饰器,通过具体的内容向大家展示,希望对大家学习Python语言有所帮助。

装饰器简介:
  装饰器本质是一个函数或者类,外部传入被装饰函数名,内部返回装饰函数名,在不修改被装饰函数源代码的前提下,扩展它的新的处理功能。装饰器经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景,装饰器是解决这类问题的绝佳设计。有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码到装饰器中并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
  备注:如果想更好的理解python装饰器,需要先掌握python闭包。
普通装饰器:
  看下述示例:

import redis
#连接redis数据库,用作缓存
pool = redis.ConnectionPool(host='localhost',port=6379,decode_responses=True)
r = redis.Redis(connection_pool=pool)

def cache(func):
    def wrapper():
        result = func()
        r.set(result["name"],result["job"]) #向数据库中插入数据
    return wrapper

@cache
def func():
    name = "小樱"
    job = "UI设计师"
    return {"name":name,"job":job}

func()
find_name = "小樱"
print("{}的工作是{}".format(find_name,r.get(find_name))) #从数据库中获取数据

#运行结果:
小樱的工作是UI设计师

  上面的cache函数就是一个装饰器了,它对原函数做了包装并返回了另外一个函数,给原函数添加了将数据存入缓存的功能。但有一个问题,如果被装饰的函数需要传入参数,那么这个装饰器就坏了,并且被装饰的函数可能多种多样,会有不同的参数。咋整?还好Python提供了可变参数*args和关键字参数**kwargs,有了这两个参数,装饰器就可以用于任意目标函数了,请看下述示例:

import redis
#连接redis数据库,用作缓存
pool = redis.ConnectionPool(host='localhost',port=6379,decode_responses=True)
r = redis.Redis(connection_pool=pool)

def cache(func):
    def wrapper(*args,**keargs):
        result = func(*args,**keargs)
        r.set(result["name"],result["job"]) #向数据库中插入数据
    return wrapper

@cache
def func(name,job):
    return {"name":name,"job":job}

func("小李","Java工程师")
find_name = "小李"
print("{}的工作是{}".format(find_name,r.get(find_name))) #从数据库中获取数据

#运行结果:
小李的工作是Java工程师

带参数的装饰器:
  假设我们前文的装饰器需要完成的功能不仅仅是将数据加入缓存,而且还需指定缓存保留的时间,那么装饰器就会是这样的:

import redis
#连接redis数据库,用作缓存
pool = redis.ConnectionPool(host='10.175.120.131',port=6379,decode_responses=True)
r = redis.Redis(connection_pool=pool)

def cache(ex):
    def wrapper(func):
        def inner_wrapper(*args,**keargs):
            result = func(*args,**keargs)
            r.set(result["name"],result["job"],ex=ex) #向数据库中插入数据,过期时间为ex=100秒
        return inner_wrapper
    return wrapper

@cache(ex = 100)  #如果这里没有使用@语法糖,等同于:func = cache(ex = 100)(func)
def func(name,job):
    return {"name":name,"job":job}

func("小李","Java工程师")
find_name = "小李"
print("{}的工作是{}".format(find_name,r.get(find_name))) #从数据库中获取数据

   如果熟悉闭包的相关知识的话,上述代码不难理解。@cache(ex = 100)实际可以理解为:运行 cache(ex = 100),返回wrapper函数,而wrapper函数实际就是个装饰器,并且夹带着ex参数,再运行wrapper(func),inner_wrapper函数就能接受ex参数,并返回inner_wrapper函数,便实现了给func函数添加数据缓存功能。
类装饰器:
  在讲类装饰前,需要先了解python的内置函数——callable:
    1.该方法用来检测对象是否可以被调用,可被调用指的是对象能否使用()括号的方法调用。
    2.可调用对象(callable(object)返回为True)在实际调用时也可能失败,但不可调用对象(callable(object)返回为False),肯定会调用失败。
    3.类对象都是可调用对象,类的实例对象是否可调用,取决于该类是否定义了 __call__方法。
  示例如下:

>>> class A:   
        pass

>>> callable(A)
True                     
>>> a = A()
>>> callable(a)
False
>>> a()
Traceback (most recent call last):
  File "", line 1, in 
    a()
TypeError: 'A' object is not callable

>>> class B:
        def __call__(self):
           print("hello")
            
>>> callable(B)
True
>>> b = B()
>>> callable(b)
True
>>> b()
hello

   了解了上述知识后,可以让类的构造函数 __init__()接受一个函数,然后重载 __call__(),并返回该函数,就可以达到装饰函数的效果,示例如下:

import time

class Time(object):
    def __init__(self,func):
        self.func = func
    def __call__(self, *args, **kwargs):
        time_start = time.time()
        self.func(*args, **kwargs)
        time_end = time.time()
        print("运行时间为{}秒".format(int(time_end - time_start)))

@Time
def func(x):
    time.sleep(x)

func(1)   #运行 func(1) 时,实际是先创建Time的实例 t = Time(func),再运行 t(1),而t(1)调用的是实例t的 __call__方法

#运行结果:
运行时间为1秒

带参数的类装饰器:
  如果需要通过类形式实现带参数的装饰器,那么会比前面的例子稍微复杂一点。在构造函数里接受的就不是一个函数,而是传入的参数。通过类把这些参数保存起来。然后在重载__call__方法是就需要接受一个函数并返回一个函数。示例如下:

import time

class Time(object):
    def __init__(self,unit):
        self.unit = unit
    def __call__(self, func):
        def wrapper(*args,**kwargs):
            time_start = time.time()
            func(*args, **kwargs)
            time_end = time.time()
            if self.unit == "毫秒":
                run_time = int((time_end - time_start)*1000)
            if self.unit == "秒":
                run_time = int(time_end - time_start)
            print("运行时间为{}{}".format(run_time,self.unit))
        return wrapper

@Time(unit = "秒")
def func(x):
    time.sleep(x)

func(2)  #运行 func(2) 时,实际是先创建Time的实例 t = Time(unit = "秒"),再运行 wrapper = t(func),而t(func)调用的是实例t的 __call__方法,最后运行wrapper(2)

#运行结果:
运行时间为2秒

本文由职坐标整理并发布,希望对同学们学习Python有所帮助,更多内容请关注职坐标编程语言Python频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 1 不喜欢 | 0
看完这篇文章有何感觉?已经有1人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程