前言Python编程语言的一大优点是它将所有功能打包到一个小包中,这非常有用。许多特性可以完全改变Python代码的功能,从而使语言更加灵活。如果使用得当,其中一些功能可以有效减少编写程序所需的时间。实现这些目标的一个很好的例子是Python的装饰器。装饰器装饰器是一种可用于更改Python函数对象行为的函数。它们可以应用于类和函数,可以做很多非常有趣的事情!装饰器可用于缩短代码、加快速度并完全改变代码在Python中的行为方式。不用说,这肯定能派上用场!今天我想展示一些我认为值得一试的装饰器。装饰器有很多,但我选择了一些我认为具有最酷功能的。1.@lru_cache此列表中的第一个装饰器来自functools模块。该模块包含在标准库中,非常易于使用。它还包含比这个装饰器更酷的功能,但这个装饰器绝对是我的最爱。此装饰器可用于加速使用缓存的功能的连续执行。当然,在使用它时应注意一些关于缓存的注意事项,但在一般用例中,大多数时候都值得使用这个装饰器。如果能够使用一个简单的装饰器来加速代码,那就太好了。可以从这种装饰器中受益的函数的一个很好的例子是递归函数,例如计算阶乘的函数:deffactorial(n):returnn*factorial(n-1)ifnelse1recursiveincomputationtime可能是非常困难,但是添加这个装饰器有助于显着加快这个函数的连续运行。@lru_cachedeffactorial(n):returnn*factorial(n-1)ifnelse1现在每当我们运行这个函数时,前几个阶乘计算将被保存到缓存中。所以下次我们调用这个函数时,我们只需要在我们之前使用的阶乘之后计算阶乘即可。当然,并不是所有的阶乘计算都会被保存,但是很容易看出为什么这个装饰器是一个很好的应用程序来加速一些本来就很慢的代码。2.@jitJIT是JustInTime的缩写。通常,每当我们在Python中运行一些代码时,首先发生的事情就是编译。此编译会产生一些开销,因为该类型已分配内存并存储为未分配但已命名的别名。使用即时编译,我们在执行时编译。在许多方面,我们可以将其视为类似于并行计算的东西,其中Python解释器同时处理两件事以节省一些时间。NumbaJIT编译器因将此概念引入Python而闻名。与@lru_cache类似,这个装饰器可以非常容易地调用并立即提高代码的性能。Numba包提供了jit装饰器,它可以更轻松地运行更密集的软件而无需进入C。以下示例使用@jit装饰器来加速MonteCarlo计算。fromnumbaimportjitimportrandom@jit(nopython=True)defmonte_carlo_pi(nsamples):acc=0foriinrange(nsamples):x=random.random()y=random.random()if(x**2+y**2)<1.0:acc+=1返回4.0*acc/nsamples3。@do_twicedo_twice装饰器的作用与它的名字所暗示的差不多。这个装饰器可用于一次调用运行一个函数两次。这当然有一些用途,而且我发现它对调试特别有用。它可以用来衡量两个不同迭代的性能。以Functools为例,我们可以运行一个函数两次来检查改进。该功能由标准库中的Python中的decorators模块提供。fromdecoratorsimportdo_twice@do_twicedeftimerfunc():%timeitfactorial(15)4.@count_callscount_calls装饰器可用于提供有关函数在软件中使用了多少次的信息。和do_twice一样,这当然可以在调试时派上用场。当添加到给定函数时,我们将收到一个输出,告诉我们该函数每次运行时已经运行了多少次。这个装饰器也在标准库的装饰器模块中。fromdecoratorsimportcount_calls@count_callsdeffunction_example():print("HelloWorld!")function_example()function_example()function_example()5.@dataclass我用来节省编写类时间的最好的装饰器之一是@dataclass装饰器。这个装饰器可以用来快速编写我们编写的类中常见的标准方法。这个装饰器来自数据类模块。此模块也在标准库中,因此无需pip即可试用此示例!fromdataclassesimportdataclass@dataclassclassFood:name:strunit_price:floatstock:int=0defstock_value(self)->float:return(self.stock*self.unit_price)这段代码会自动创建一个初始化函数init(),其中包含填充类中数据所需的位置参数。它们也会自动提供给自己,所以不需要写一个很长的函数来把一些数据参数放到类中。6.@singleton为了理解单例装饰器的用途,我们首先需要了解什么是单例。从某种意义上说,单例是全局变量类型的一个版本。这意味着该类型被定义为只存在一次。虽然这些在C++等语言中很常见,但在Python中很少见。使用单例,我们可以创建一个只使用一次的类并改变该类,而不是通过初始化构造一个新类型。通常,单例装饰器是用户自己编写的,并不实际导入。这是因为单例仍然是对单例装饰器中提供的模板的引用。我们可以命名一个单例函数并编写一个包装器以在我们的类上使用这个装饰器:defsingleton(cls):instances={}defwrapper(*args,\*\*kwargs):ifclsnotininstances:instances[cls]=cls(*args,\*\*kwargs)returninstances[cls]returnwrapper@singletonclasscls:deffunc(self):另一种方法是使用元类!7.@use_unitin在科学计算中经常派上用场的一个装饰器是@use_unit装饰器。这个装饰器可以用来改变返回结果的表示单元。这对于那些不想向其数据添加度量单位但仍希望人们知道这些单位是什么的人很有用。这个装饰器在任何模块中都不是真正可用的,但它在科学应用中非常常见和有用。defuse_unit(unit):"""让函数返回给定单位的数量"""use_unit.ureg=pint.UnitRegistry()defdecorator_use_unit(func):@functools.wraps(func)defwrapper_use_unit(*args,\*\*kwargs):value=func(*args,\*_kwargs)返回值_use_unit。ureg(unit)returnwrapper_use_unitreturndecorator_use_unit@use_unit("meterspersecond")defaverage_speed(distance,duration):返回距离/duration8。@singledispatchFunctools凭借非常有用的@singledispatch装饰器再次在此列表中脱颖而出。Single-dispatch是一种编程技术,在许多编程语言中都很常见,因为它是一种很棒的编程方式。虽然我更喜欢multi-dispatch,但我认为single-dispatch可以在很多方面发挥同样的作用。这个装饰器使得在Python中处理多种类型的数据变得更加容易,特别是当我们想通过相同的方法传递多种类型的数据时。@singledispatchdeffun(arg,verbose=False):ifverbose:print("Letmejustsay,",end="")print(arg)@fun.registerdef\_(arg:int,verbose=False):ifverbose:print("数字强度,嗯?",end="")print(arg)@fun.registerdef\_(arg:list,verbose=False):ifverbose:print("Enumeratethis:")fori,eleminenumerate(arg):打印(i,elem)
