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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Kaptcha 验证码框架使用

    Kaptcha 验证码框架使用基于springboot验证码框架kaptcha使用一、统一步骤引入maven坐标<dependency><groupId>com.github.penggle</groupId><artifactId>kaptcha</artifactId>…

    2022年6月18日
  • 培训师电梯销售法则-三句半「建议收藏」

    培训师电梯销售法则-三句半「建议收藏」今天在北京一个神奇的培训中心开发一门无线接入技术的培训课件,开发完毕以后,进行了课程试讲,总体试讲的情况还是很不错的,但是在总结阶段,效果并不理想,因此辅导老师最后给出了一个电梯销售法则-三句半,下面首先介绍一下电梯销售法则: “麦肯锡30秒电梯理论”来源于麦肯锡公司一次沉痛的教训。  麦肯锡公司曾经得到过一次沉痛的教训:该公司曾经为一家重要的大客户做咨询。咨询结束…

    2022年5月27日
  • .NET WinForm中使用Timer定时更新ProgressBar的进度「建议收藏」

    .NET WinForm中使用Timer定时更新ProgressBar的进度

    2022年3月11日
  • Linux中创建自己的MOTD[通俗易懂]

    Linux中创建自己的MOTD[通俗易懂]1)什么是MOTDmotd:是英文缩写messageoftheday。译文是:每日提示信息,问候报文。那你为什么要用MOTD?其实目的很简单,是提示进入系统的用户注意事项,或提示系统运行的概

    2022年8月3日
  • Appium – iOS Mac环境结构

    Appium – iOS Mac环境结构

    2022年1月7日
  • ESP32-Drone四旋翼无人机代码编译发现的二个问题及解决方法

    ESP32-Drone四旋翼无人机代码编译发现的二个问题及解决方法摘要ESP32-Drone四旋翼无人机是乐鑫的一个开源项目,我根据官方的硬件参考设计,重新使用KiCAD绘制了原理图和PCB板,并制作了控制板样板,在配置了ESP-idf-4.4编程环境编译官方的软件包时遇到了二个比较严重的问题,具体过程记录如下:编译问题1:找不到FreeRTOS.h头文件。如图1-1所示,在编译的过程中,发现报没有找到FreeRTOS.h头文件,这应该是C语言程序编译过程中常见的头文件目录环境变量的设置有问题。如图1-2所示,根据报错信息的提示,找到“craz.

    2022年8月15日

发表回复

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

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