Python装饰器

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

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

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

  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语句代替

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

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

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • linux命令行怎么结束进程,linux结束进程命令「建议收藏」

    linux命令行怎么结束进程,linux结束进程命令「建议收藏」linux下进程的开启与结束都可以通过命令来控制,下面由学习啦小编为大家整理了linux下结束进程命令的相关知识,希望对大家有所帮助。linux结束进程命令1.killkill[信号代码]根据PID向进程发送信号,常用来结束进程,默认信号为-9信号代码,可取值如下:-l[信号数字]显示、翻译信号代码-9,-KILL发送kill信号退出-6,-ABRT发送abort信号…

    2022年10月30日
    0
  • Django模板系统「建议收藏」

    Django模板系统「建议收藏」Django模板系统官方文档常用语法只需要记两种特殊符号:{{}}和{%%}变量相关的用{{}},逻辑相关的用{%%}。变量{{变量名}}变量名由字母数字和下划线组成。点(.)在模板语言中有特殊的含义,用来获取对象的相应属性值。几个例子:view中代码:deftemplate_test(request):l=[11,2…

    2022年7月19日
    12
  • Maven安装教程_vmware安装教程

    Maven安装教程_vmware安装教程一、安装apache官网下载需要的版本,然后解压缩,解压路径尽量不要有空格和中文Maven–DownloadApacheMavenhttps://maven.apache.org/download.cgi二、配置环境变量新建系统变量变量名:MAVEN_HOME值:你解压的路径eg:D:\xxx\apache-maven-3.8.4编辑系统变量Path打开->新建->路径为:解压缩文件的路径到bin目录eg:D:\xxx\apac

    2022年8月22日
    4
  • Java生成xml文件的四种方式

    Java生成xml文件的四种方式上一篇文章写了xml文件4种读取方式,本篇文章将简介使用四种方式对xml进行写入。

    2022年7月21日
    9
  • 常见的图片类型_金鱼种类图片

    常见的图片类型_金鱼种类图片1、Jpg(jpeg):这是一种可以高度保留图片色彩信息的格式2、Png:该类型的图片可以实现透明3、Gif:图片所占体积小,可以实现动图4、Psd:它是一种分层的图片

    2022年8月3日
    4
  • PyCharm的安装和配置(社区版)「建议收藏」

    PyCharm的安装和配置(社区版)「建议收藏」Python学习基础笔记一、PyCharm的安装和配置二、Python基础语法2.1、概率2.2、标识符一、PyCharm的安装和配置二、Python基础语法2.1、概率2.2、标识符

    2022年8月26日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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