• 10个美妙的Python装饰器


    10个美妙的Python装饰器 对Python编程语言中我最喜欢的一些装饰器的概述。

    简介

    关于Python编程语言的伟大之处在于,它在一个小包里装了所有的功能,这些功能非常有用。很多特性可以完全改变Python代码的功能,这使得该语言更加通用。此外,如果使用得当,这些功能中的一些可以起到缩短编写有效软件的时间的作用。Python的装饰器是一个很好的例子,它很好地完成了这些目标。

    装饰器是快速编程宏,可以用来改变 Python 对象的行为。它们可以应用于类和函数,并且实际上可以做很多非常有趣的事情。装饰器可以用来缩短代码,加快代码速度,并完全改变代码在Python中的行为方式。不用说,这肯定能派上用场! 今天我想展示一些我认为值得一看的装饰器。有很多装饰器,但我挑选了一些我认为功能最酷的。

    №1: @lru_cache

    这个列表中的第一个装饰器来自functools模块。这个模块包含在标准库中,而且非常容易使用。除了这个装饰器之外,它还包含了很多很酷的功能,但这个装饰器无疑是我最喜欢的。

    @lru_cache装饰器可以用来加快函数和操作的连续运行速度,使用缓存。当然,在使用时要注意交换和缓存的一些注意事项,但在通用的使用情况下,大多数时候这个装饰器是值得使用的。如果你想了解更多关于Functools和我为什么喜欢它,实际上我写了一整篇文章,你可以在这里阅读。

    FuncTools:一个被低估的Python包 用Functools把你的Python函数提升到一个新的水平!

    能够用一个简单的装饰器来加快代码的速度是令人难以置信的。一个可以从这样的装饰器中受益的函数的很好的例子是递归函数,比如这个计算阶乘的函数。

    def factorial(n):
        return n * factorial(n-1) if n else 1
    • 1

    递归对于计算时间来说是相当困难的,但是添加这个装饰器可以帮助大大加快这个函数的连续运行速度。

    @lru_cache
    def factorial(n):
        return n * factorial(n-1) if n else 1
    • 1

    现在,每当我们运行这个函数时,前几个阶乘的计算将被保存到缓存中。因此,下次我们再去调用这个函数时,只需要计算我们之前使用的阶乘之后的阶乘。当然,并不是所有的阶乘计算都会被保存,但是很容易看出为什么这是一个很好的应用,这个装饰器可以加速一些自然缓慢的代码。

    №2: @jit

    JIT是Just In Time编译的简称。通常我们在Python中运行一些代码时,首先发生的是编译。这种编译产生了一些开销,因为类型被分配了内存,并以未分配但命名的别名的形式存储。有了及时编译,我们在执行时就完成了大部分的工作。在很多方面,我们可以认为这类似于并行计算,Python 解释器同时处理两件事情,以节省一些时间。

    Numba JIT编译器就是因为在Python中提供了这个概念而闻名。与@lru_cache类似,这个装饰器可以很容易地被调用,在你的代码中可以立即提升性能。Numba包提供了jit装饰器,这使得运行更密集的软件变得更容易,而不需要落入C语言。

    from numba import jit
    import random

    @jit(nopython=True)
    def monte_carlo_pi(nsamples):
        acc = 0
        for i in range(nsamples):
            x = random.random()
            y = random.random()
            if (x ** 2 + y ** 2) < 1.0:
                acc += 1
        return 4.0 * acc / nsamples

    • 1

    №3: @do_twice

    do_twice装饰器的作用与它的名字差不多。这个装饰器可以被用来在一次调用中运行一个函数两次。这当然有一些用途,我发现它对调试特别有帮助。它的另一个用途是测量两个不同迭代的性能。以Functools为例,我们可以让一个函数运行两次,以便检查出现的改进。这个函数是由Python中的装饰器模块提供的,它在标准库中。

    from decorators import do_twice
    @do_twice
    def timerfunc():
        %timeit factorial(15)
    • 1

    №4: @count_calls

    与do_twice装饰器的简单性相一致的是count_calls装饰器。这个装饰器可以用来提供一个函数在软件中被使用多少次的信息。

    和do_twice一样,这在调试时肯定会派上用场。当添加到一个给定的函数时,我们将收到一个输出,告诉我们该函数在每次运行时被运行了多少次。这个装饰器也在标准库的装饰器模块中。

    from decorators import count_calls
    @count_calls
    def function_example():
        print("Hello World!"
    function_example()
    function_example()
    function_example()
    • 1

    №5: @dataclass

    为了在编写类时节省时间,我一直利用的最好的装饰器之一是数据类装饰器。这个装饰器可以用来快速编写常见的标准方法,这些方法通常在我们编写的类中出现。如果你想进一步了解这个装饰器,我也有一篇关于它的文章,你可以在这里阅读。

    这个装饰器来自于dataclass模块。这个模块也在标准库中,所以没有必要用PIP来尝试这个例子

    from dataclasses import dataclass@dataclass
    class Food:
        name: str
        unit_price: float
        stock: int = 0
            
        def stock_value(self) -> float:
            return(self.stock * self.unit_price)
    • 1

    这段代码将自动创建一个初始化函数,__init__(),其中有必要的位置参数来填充我们类中的数据。它们也将自动提供给self,所以没有必要为了在类中获得一些数据参数而写一个很长的函数。

    №6: @singleton

    为了理解 @singleton装饰器的目的,我们首先需要理解什么是singleton。singleton在某种意义上是全局类型的一个版本。这意味着,这些类型被定义为只存在一次。

    尽管这些类型在像 C++ 这样的语言中很常见,但在 Python 中却很少见到。对于singleton,我们创建一个只使用一次的类,并对该类进行突变,而不是初始化构造类型。在这种情况下,类型的作用不像是模板,而更像是一个单一的受控对象。

    通常情况下,singleton装饰器是由用户制作的,事实上并没有导入。这是因为单子仍然是对我们的单子装饰器中提供的模板的引用。我们可以命名一个单子函数并编写一个包装器,以便在我们的类上使用这个装饰器。

    def singleton(cls):
        instances = {}
        def wrapper(*args, **kwargs):
            if cls not in instances:
              instances[cls] = cls(*args, **kwargs)
            return instances[cls]
        return wrapper
    @singleton
    class cls:
        def func(self):
    • 1

    另一种方法是通过使用元类来解决这个问题。如果你想了解更多关于元类的信息,我在去年写了一篇文章,详细介绍了这些元类和它们的用途,你可以在这里查看。

    class Singleton(type):
        _instances = {}
        def __call__(cls, *args, **kwargs):
            if cls not in cls._instances:
                cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
            return cls._instances[cls]
            
    class Logger(object):
        __metaclass__ = Singleton
    • 1

    №7: @use_unit

    对于科学计算来说,有一个装饰器可能经常派上用场,那就是use_unit装饰器。这个装饰器可以用来改变返回方法的输出。

    这对于那些不想给他们的数据添加测量单位,但仍想让人们知道这些单位是什么的人来说是很有用的。这个装饰器在任何模块中都不是真正可用的,但它是非常常用的,对科学应用来说非常有用。

    def use_unit(unit):
        "让一个函数返回一个给定单位的数量"
        use_unit.ureg = pint.UnitRegistry()
        def decorator_use_unit(func):
            @functools.wraps(func)
            def wrapper_use_unit(*args, **kwargs):
                value = func(*args, **kwargs)
                return value * use_unit.ureg(unit)
            return wrapper_use_unit
        return decorator_use_unit

    @use_unit("meters per second")
    def average_speed(distance, duration):
        return distance / duration
    • 1

    №7: @wraps

    函数包装器 @wraps是一种设计模式,用于处理Python中相对复杂的函数。封装函数通常用来做一些可能被认为是更低级的、迭代的任务。封装函数的好处是,它可以用来极大地提高性能。和lru_cache装饰器一样,这个装饰器是由FuncTools包提供的。

    import functools as ft
    • 1

    wraps装饰器本身只是一个方便的装饰器,用于更新一个给定函数的包装器。当这个装饰器被调用时,该函数将在每次使用时更新其包装器。这对提高性能有很大的帮助,FuncTools中的许多工具都是如此。

    def my_decorator(f):
        @wraps(f)
        def wrapper(*args, **kwds):
            print('Calling decorated function')
            return f(*args, **kwds)
        return wrapper
    • 1

    @wraps本身就是典型的装饰器,wrapper装饰器的调用通常用于该装饰器调用中的一个包装器函数。写完这段代码后,我们就可以用那个包装器来装饰我们的新函数了。

    @my_decorator
    def func(x):
        print(x)   
      
    • 1

    №8: @staticmethod

    在某些情况下,人们可能希望能够访问那些被私下定义的东西,就像在一个更全局的意义上。有时我们有一些包含在类中的函数,我们希望将其方法化,而这正是staticmethod装饰器的用途。

    使用这个装饰器,我们可以使C++静态方法与我们的类一起工作。通常情况下,写在类的范围内的方法对该类来说是私有的,除非作为子类调用,否则无法访问。然而,在某些情况下,你可能希望采取一种更实用的方式来处理你的方法与数据的交互。使用这个装饰器,我们可以创建这两个选项,而不需要创建一个以上的函数。

    class Example:
        @staticmethod
        def our_func(stuff):
            print(stuff)     
    • 1

    我们也不需要明确地提供我们的类作为一个参数。staticmethod装饰器为我们处理了这个问题。

    №9: @singledispatch

    FuncTools在这个列表中再次出击,推出了非常有用的singledispatch装饰器。单一调度是一种编程技术,在许多编程语言中都很常见,因为它是一种很好的编程方式。虽然我更倾向于多路调度,但我认为单路调度在很多方面都可以发挥同样的作用。

    这个装饰器使得在 Python 中处理类型要容易得多。当我们在处理要通过同一个方法的多个类型时,情况更是如此。我在我的FuncTools文章中写了更多关于这个问题的内容,所以如果你对使用单一派发方法感兴趣,我确实推荐你使用(链接在#1。)

    @singledispatch
    def fun(arg, verbose=False):
            if verbose:
                print("Let me just say,", end=" ")
            print(arg)
    @fun.register
    def _(arg: int, verbose=False):
        if verbose:
            print("Strength in numbers, eh?", end=" ")
        print(arg)
    @fun.register
    def _(arg: list, verbose=False):
        if verbose:
            print("Enumerate this:")
        for i, elem in enumerate(arg):
            print(i, elem)
    • 1

    №10: @register

    注册函数来自于模块atexit。考虑到该模块的名称和工作原理,你可能会想到这个装饰器可能与终止时执行某些动作有关。

    register装饰器命名了一个终止时要运行的函数。例如,这将与一些需要在你退出时进行保存的软件一起工作。

    from atexit import register
    @register
    def termin():
        print(" Goodbye!" )
    • 1

    总结

    不用说,Python 的装饰器是非常有用的。它们不仅可以用来减慢编写一些代码的时间,而且对加快代码的速度也有很大的帮助。当你发现装饰器的时候,不仅是令人难以置信的有用,而且编写你自己的装饰器也是一个好主意。

    这些装饰器之所以强大,是因为装饰器作为一种特性在 Python 中非常强大。感谢你阅读我的文章,我希望它能让你注意到Python的一些很酷的能力!

    本文由 mdnice 多平台发布

  • 相关阅读:
    前端基础知识点:JS中的参数传递详解
    Vue3最佳实践 第七章 TypeScript 中
    戴尔G3-3579改固态散热
    vue api封装
    [附源码]JAVA毕业设计酒店管理系统(系统+LW)
    SpringBoot 过滤器和拦截器的区别
    Spring Cloud Alibaba(四)
    曲线艺术编程 coding curves 第十四章 其它曲线(Miscellaneous Curves)
    搭建Python开发环境
    pytorch -- torch.nn.Module
  • 原文地址:https://blog.csdn.net/qq_40523298/article/details/127550758