当前位置: 首页 > 科技观察

一个已经存在了10年却被严重低估的库

时间:2023-03-12 05:49:41 科技观察

本文转载自微信公众号《码哥铭》(Cool-Python)。大家好,我是明哥。今天给大家介绍一个已经存在了13年但依旧不火的librarydecorator。似乎很少有人知道它的存在。这个图书馆能为您做什么?其实很简单,可以帮助你更方便的编写python装饰器代码,更重要的是,它让Python中装饰器装饰的方法看起来更像装饰前的方法。本文不会过多向您介绍装饰器的基础知识。我假设您知道装饰器是什么以及如何编写简单的装饰器。如果你对装饰器不了解,可以看看我之前写的那篇文章,非常全面详细的介绍了装饰器的各种实现方式。1.常规装饰器下面是一个最简单的装饰器例子,运行myfunc函数前后会打印一条log。defdeco(func):defwrapper(*args,**kw):print("Readytoruntask")func(*args,**kw)print("Successfultoruntask")returnwrapper@decodefmyfunc():print("Runningthetask")myfunc()decorator用起来好像有点高端和神奇。对于一些重复的功能,我们往往会封装成一个装饰器函数。在定义装饰器的时候,我们都需要像上面那样机械地写一个嵌套函数。对装饰器原理理解不深刻的初学者,往往过一段时间就忘记了如何定义装饰器。一些比较聪明的同学会用PyCharm自动生成装饰器模板,想用的时候直接敲deco就可以生成一个简单的生成器代码,提高编码准备的效率。2.使用神库使用PyCharm的LiveTemplate,虽然可以降低编写装饰器的难度,但是依赖于PyCharm这个专业的代码编辑器。在这里,明哥要教大家一个更简单的方法。使用这种方法需要先安装一个库:decorator,使用pip即可轻松安装$python3-mpipinstalldecorator从库的名字不难看出,这是一个专门为解决该问题而设计的第三方库装饰器问题。拥有它之后,你会惊奇地发现,以后再也不用为自己定义的装饰器写嵌套函数了。fromdecoratorimportdecorator@decoratordefdeco(func,*args,**kw):print("Readytoruntask")func(*args,**kw)print("Successfultoruntask")@decodefmyfunc():print("Runningthetask")myfunc()装饰作为装饰函数,第一个参数是固定的,指的是装饰函数,后面的可变参数的参数是固定的,使用可变参数*args和**kw的写法,代码为用函数的原始参数装饰。不得不说,这样的写法更直观,代码的逻辑也更容易理解。3.有带参数的装饰器吗?根据是否携带参数,装饰器可以分为两种类型。kw):func(*args,**kw)returnwrapper第二种:带参数,比较复杂,不是那么好理解。defdecorator(arg1,arg2):defwrapper(func):defdeco(*args,**kwargs)func(*args,**kwargs)returndecoreturnwrapper那么对于需要参数的装饰器,decorator是不是也能很好的支持呢??下面是官方的例子来自decoratorimportdecorator@decoratordefwarn_slow(func,timelimit=60,*args,**kw):t0=time.time()result=func(*args,**kw)dt=time.time()-t0ifdt>timelimit:logging.warn('%stook%dseconds',func.__name__,dt)else:logging.info('%stook%dseconds',func.__name__,dt)returnresult@warn_slow(timelimit=600)#warnifittakesmorethan10minutesdefrun_calculation(tempdir,outdir):pass可以看到:装饰函数的第一个参数还是decoratorfunc,和之前一样,第二个参数timelimit写成位置参数,有默认值对于后面,不难推导出可变参数的写法还是和之前一样。只要在装饰函数中从第二个参数开始,使用不可变参数的写法,这些参数就可以作为调用装饰器时的参数。.4.签名问题解决了吗?我们在编写自己的装饰器时,通常会添加一个装饰器,名称为functools.wraps。我想你应该经常看到它,那它有什么用呢?我们先来看一下。示例defwrapper(func):definner_function():passreturninner_function@wrapperdefwrapped():passprint(wrapped.__name__)#inner_function为什么会这样?不应该返回func吗?这个不难理解,因为上边执行func和下边的decorator(func)是等价的,所以上面的func.__name__等价于下面的decorator(func).__name__,当然名字是inner_functiondefwrapper(func):definner_function():passreturninner_functiondefwrapped():passprint(wrapper(wrapped).__name__)#inner_function目前我们可以看到,当一个函数被装饰器装饰后,其签名信息会发生变化(如上面看到的函数名),那么如何避免这种情况呢?解决办法就是使用我们前面说的functools.wraps装饰器。它的作用是将修饰函数(wrapped)的一些属性值赋值给装饰函数(wrapper),最终让属性的展示更符合我们的直觉。fromfunctoolsimportwrapsdefwrapper(func):@wraps(func)definner_function():passreturninner_function@wrapperdefwrapped():passprint(wrapped.__name__)#wrapped那么问题来了,我们使用装饰器之后,还会出现这样的签名问题吗??写个例子验证一下。fromdecoratorimportdecorator@decoratordefdeco(func,*args,**kw):print("Readytoruntask")func(*args,**kw)print("Successfultoruntask")@decodefmyfunc():print("Runningthetask")打印的输出(myfunc.__name__)是myfunc,也就是说装饰器默认已经帮我们处理了所有可以预见的问题。5.综上所述,decorator是一个提高装饰器编码效率的第三方库。适合对装饰器原理一头雾水的新手,让你轻松写出更符合人的直觉的代码。带参数的装饰器的定义非常复杂。需要编写多层嵌套的函数,需要熟悉每个参数的传递路径,才能保证你写的装饰器可以正常使用。这时候只要使用装饰器库,就可以轻松编写带参数的装饰器。同时,您也不必担心他的签名问题,已经为您妥善处理。这么棒的库,推荐大家使用。