Python装饰器

装饰器表现形式1.函数装饰器编写自定义装饰器有许多方法,但最简单的方法是编写一个函数,返回包装原始函数调用的一个子函数例1:>>>[DEBUG]:entersay_he

大家好,又见面了,我是全栈君,今天给大家准备了Idea注册码。

Python装饰器此处内容已经被作者隐藏,请输入验证码查看内容
验证码:
请关注本站微信公众号,回复“”,获取验证码。在微信里搜索“”或者“”或者微信扫描右侧二维码都可以关注本站微信公众号。

  Python装饰器的作用是使函数包装和方法包装变得更容易阅读和理解,最常见的就是@staticmethod和@classmethod,下面将从装饰器的表现形式和常用装饰器模式两方面进行描述和总结,若有不正确之处望大家指出。

装饰器表现形式

1. 函数装饰器

  编写自定义装饰器有许多方法,但最简单的方法是编写一个函数,返回包装原始函数调用的一个子函数

  例1:

#coding=utf-8

def debug(func):
    def wrapper(*agrs, **kwargs):
        '''包装函数内部文档''' 
        print ("[DEBUG]:enter %s()--%s" %(func.__name__, *agrs))
        return func(*agrs, **kwargs)
    return wrapper
@debug
def say_hello(parm): ''' 提供函数文档字符串''' print ("say_hello") if __name__ == "__main__": say_hello("Python") print ("原始函数名:%s" %(say_hello.__name__)) print ("函数文档字符串:%s" %(say_hello.__doc__))

>>> [DEBUG]:enter say_hello()–Python
>>> say_hello
>>> 原始函数名:wrapper
>>> 函数文档字符串:包装函数内部文档

  例2:

#coding=utf-8

from functools import wraps

def debug(func):
    @wraps(func)
    def wrapper(*agrs, **kwargs):
        '''包装函数内部文档''' 
        print ("[DEBUG]:enter %s()--%s" %(func.__name__, *agrs))
        return func(*agrs, **kwargs)
    return wrapper

@debug
def say_hello(parm):
    ''' 提供函数文档字符串'''
    print ("say_hello")
    
if __name__ == "__main__":
    say_hello("Python")
    print ("原始函数名:%s" %(say_hello.__name__))
    print ("函数文档字符串:%s" %(say_hello.__doc__))

>>> [DEBUG]:enter say_hello()–Python
>>> say_hello
>>> 原始函数名:say_hello
>>> 函数文档字符串: 提供函数文档字符串

注意例1与例2的区别,也是使用装饰器的常用错误,在使用装饰器时不保存函数元数据(文档字符串和原始函数名)

2. 类作为装饰器

  虽然装饰器几乎总是可以用函数来实现,但如果装饰器需要复杂的参数化或者依赖特定状态的话,使用自定义类进行封装可能会更好

#coding=utf-8

from functools import wraps

class debug:
    def __init__(self, func):
        self.func = func

    def __call__(self, *argv, **kwargv):
        '''包装函数内部文档'''
        print ("[DEBUG]:enter %s()--%s" %(self.func.__name__, *argv))
        self.func(*argv, **kwargv)

def say_hello(something):
    ''' 提供函数文档字符串 '''
    print ("say_hello", something)
    
if __name__ == "__main__":
    De = debug(say_hello)
    De("Python")
    print ("原始函数名:%s" %(say_hello.__name__))
    print ("函数文档字符串:%s" %(say_hello.__doc__))

>>> [DEBUG]:enter say_hello()–Python
>>> say_hello Python
>>> 原始函数名:say_hello
>>> 函数文档字符串: 提供函数文档字符串

3. 参数化装饰器

  在实际代码中通常需要使用参数化的装饰器,比如次数、类型判断等,下面是一个简单的装饰器示例,给定重复次数,每次被调用时都会重复执行被装饰函数

#coding=utf-8

from functools import wraps

#参数化装饰器
def repeat(number=3):
    def debug(func):
        @wraps(func)
        def wrapper(*argv, **kwargv):
            '''包装函数内部文档'''
            for _ in range(number):
                print ("[DUBEG]:enter %s()--%s" %(func.__name__, *argv))
                result = func(*argv, **kwargv)
            return result
        return wrapper 
    return debug    

@repeat(2)
def say_hello(*agrv, **kwargv):
    '''提供函数文档字符串'''
    print ("say_hello")
    
if __name__ == "__main__":  
    say_hello("Python")
    print ("原始函数名:%s" %(say_hello.__name__))
    print ("函数文档字符串:%s" %(say_hello.__doc__))

>>> [DUBEG]:enter say_hello()–Python
>>> say_hello
>>> [DUBEG]:enter say_hello()–Python
>>> say_hello
>>> 原始函数名:say_hello
>>> 函数文档字符串:提供函数文档字符串

4. 装饰器装饰类

  和装饰一个函数类似,也可以写一个函数来装饰类,用来向类中添加功能,基本原则一致,装饰器是一个函数或是一个可调用对象,它接受一个类作为参数,返回一个类作为返回值

#coding = utf-8

def decoratortest(cls):
    print ("{0.__class__.__qualname__}".format(cls))
    return cls

@decoratortest
class testclass:
    def __init__(self, value):
        self.value = value

    def __repr__(self):
        return "{0}:88".format(self)    

if __name__ == "__main__":
 
    t = testclass(88)

常用装饰器模式

1. 参数检查

  将函数注册到全局字典中,并将其参数和返回值保存在一个类型列表中,并对参数类型进行检测

#coding=utf-8

'''将函数注册到全局字典中,并将其参数和返回值保存在一个类型列表中'''

funname = {}
def parmcheck(in_= (type(None),), out_ =(type(None), )):
    def fun1(func):
        func_name = func.__name__
        print ("funname:%s" %(func_name))
        funname[func.__name__] = (in_, out_)
        def checkType(elements, types):
            '''用来检查参数类型的子函数'''
            if len(elements) != len(types):
                raise TypeError("Parm count is wrong!")
            li = zip(elements, types)
            typed = enumerate(li)
            for index,couple in typed:
                argv, intype = couple
                if isinstance(argv, intype):
                    print ("parm(%s) and type(%s)are all right" %(argv, intype))
                    continue
                raise TypeError("argv %d should be %s" %(argv, intype))    
                
        def decoratorfun(*argv):
            #types = [type(i) for i in range(len(argv))]
            #checkType(argv, types)
            checkType(argv, in_)
            res = func(*argv)
            #检查输出内容
            if type(res) not in (tuple, list):
                checkable_res = (res, )
            else:
                checkable_res = res
            checkType(checkable_res, out_)    
        return decoratorfun
    return fun1    
             
@parmcheck((int,int)) 
def meth1(a,b):
    print ("received:%d,%d" %(a, b))

if __name__=="__main__":
    meth1(1,2)
    print (funname)

>>> funname:meth1
>>> parm(1) and type(<class ‘int’>)are all right
>>> parm(2) and type(<class ‘int’>)are all right
>>> received:1,2
>>> parm(None) and type(<class ‘NoneType’>)are all right
>>> {‘meth1’: ((<class ‘int’>, <class ‘int’>), (<class ‘NoneType’>,))}

注意zip、enumerate、解包、isinstance方法的使用

2. 缓存

  缓存装饰器与参数检查十分相似,它的重点是关注那些内部状态不会影响输出的函数,每组参数都可以连接到唯一的结果

#coding = utf-8

import time
import pickle
import hashlib

#全局字典
cache = {}

def is_obsolete(entry, duration):
    print (time.time() - entry["time"])
    return time.time() - entry["time"] > duration

def compute_key(func, *argv, **kwargv):
    key = pickle.dumps((func.__name__, argv, kwargv))
    return hashlib.sha1(key).hexdigest()

def memoize(duration=10):
    def _memoize(func):
        def __memoize(*argv, **kwargv):
            key = compute_key(func,*argv, **kwargv)
            if ((key in cache) and not is_obsolete(cache[key], duration)):
                print ("we got a winner")
                return cache[key]['value']    
            result = func(*argv, **kwargv)
            cache[key]={"value":result,"time":time.time()}
            return result
        return __memoize
    return _memoize    

@memoize(3)
def fun(a,b):
    print (a+b)
    return a+b

if __name__=="__main__":
    fun(2,3)
    fun(2,2)
    fun(2,3)
    print (cache)
        

>>> 5
>>> 4
>>> 0.0
>>> we got a winner
>>> {‘a99634a4e619a2ad129df1b51002a8c0cb9cca2b’: {‘value’: 5, ‘time’: 1518243058.456
>>> 425}, ‘99683ddc4e22fd3f37e473de5d61699a5c27c2c6’: {‘value’: 4, ‘time’: 151824305
>>> 8.456425}}

3. 代理

  代理装饰器使用全局机制来标记和注册函数。比如一个根据当前用户来保护代码访问的安全层可以使用集中式检查器和相关的可调用对象要求的权限来访问,这一模型常用于Python Web框架中,用于定义法布类的安全性

4. 上下文提供者

   上下文装饰器确保函数可以允许在正确的上下文中,比如临界资源的使用

#coding=utf-8

from threading import RLock

lock = RLock()

def synchronized(func):
    def _synchronized(*argv, **kdargv):
        lock.require()
        try:
            return func(*argv, **kdargv)
        except:
            print ("fun error!!!")
        finally:
            lock.release()
    return _synchronized    

上下文装饰器通常会被上下文管理器with语句代替

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/120210.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • matlab用插值法plot,Matlab插值法

    matlab用插值法plot,Matlab插值法实验目的:1.Matlab中多项式的表示及多项式运算2.用Matlab实现拉格朗日及牛顿插值法3.用多项式插值法拟合数据实验要求:1.掌握多项式的表示和运算2.拉格朗日插值法的实现(参见吕同富版教材)3.牛顿插值法的实现(参见吕同富版教材)实验内容:1.多项式的表达式和创建;多项式的四则运算、导数与积分。2.用Matlab实现拉格朗日及牛顿插值法。3.用多项式插值法拟合数据。实验步骤:1.多项式的…

  • FTP客户端FlashFXP激活成功教程教程[通俗易懂]

    FTP客户端FlashFXP激活成功教程教程[通俗易懂]flashfxp5.4下载地址:https://www.flashfxp.com/download激活成功教程工具下载地址:链接:https://pan.baidu.com/s/17Dq_OnH-ua9fbOuvMIdRXQ密码:laat下载之后然后正常安装就行了,安装完毕之后,先别打开软件,激活成功教程工具下载之后,包含两个文件,解压到flashfxp的安装位置根目录,如下:双击打开Fl…

  • 像素密度(衡量屏幕显示能力)

    像素密度(衡量屏幕显示能力)像素密度=√[(长度像素数)^2+(宽度像素数)^2]/屏幕尺寸eg:

  • Latex——在线快速生成表格代码

    Latex——在线快速生成表格代码latex在线生成表格的网站:http://www.tablesgenerator.com/latex_tables这个网站可以通过三种方式来生成latex表格代码:1、自己设置表格;2、直接导入csv表格;3、直接复制表格内容1、自己设置表格点击File,选择newtables,可以设置需要的行列数2、点击importcsvfile,可以直接导入3、点击paste…

  • Xmind快捷键详解(思维导图版)

    Xmind快捷键详解(思维导图版)说明软件版本:XMind8Update7操作系统:Windows10剪切画:部分剪切画从外部导入快捷键

  • SVN服务器迁移操作「建议收藏」

    SVN服务器迁移操作「建议收藏」SVN服务器迁移,多资源库合并

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号