乐趣区

关于程序员:9个Python-内置装饰器-显著优化代码

装璜器是利用“Python 之禅”哲学的最佳 Python 个性。装璜器能够帮忙您编写更少、更简略的代码来实现简单的逻辑并在任何中央重用它。

更重要的是,有许多很棒的内置 Python 装璜器使咱们的生存变得更加轻松,因为咱们只需应用一行代码就能够为现有的函数或类增加简单的性能。

让咱们看看我精心筛选的 9 个装璜器,本文将向您展现 Python 是如许优雅。

1. @lru_cache

应用缓存技巧减速 Python 函数的最简略办法是应用 @lru_cache 装璜器。

这个装璜器能够用来缓存一个函数的后果,这样后续调用雷同参数的函数就不会再执行了。它对于计算量大或应用雷同参数频繁调用的函数特地有用。

让咱们看一个直观的例子:

import time


def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)


start_time = time.perf_counter()
print(fibonacci(30))
end_time = time.perf_counter()
print(f"The execution time: {end_time - start_time:.8f} seconds")
# The execution time: 0.18129450 seconds

下面的程序应用 Python 函数计算第 N 个斐波那契数。计算 fibonacci(30) 的时候很耗时,很多后面的 Fibonacci 数在递归过程中会计算很屡次。

当初,让咱们应用 @lru_cache 装璜器来减速它:

from functools import lru_cache
import time


@lru_cache(maxsize=None)
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)


start_time = time.perf_counter()
print(fibonacci(30))
end_time = time.perf_counter()
print(f"The execution time: {end_time - start_time:.8f} seconds")
# The execution time: 0.00002990 seconds

如上代码所示,应用 @lru_cache 装璜器后,咱们能够在 0.00002990 秒内失去雷同的后果,比之前的 0.18129450 秒快了很多。

@lru_cache 装璜器有一个 maxsize 参数,指定要存储在缓存中的最大后果数。当缓存已满并且须要存储新后果时,最近起码应用的后果将从缓存中逐出认为新后果腾出空间。这称为最近起码应用 (LRU) 策略。

默认状况下,maxsize 设置为 128。如果将其设置为 None,如咱们的示例,LRU 性能将被禁用,并且缓存能够有限增长。

2. @total_ordering

functools 模块中的 @total_ordering 装璜器用于依据定义的办法为 Python 类生成短少的比拟办法。

from functools import total_ordering


@total_ordering
class Student:
    def __init__(self, name, grade):
        self.name = name
        self.grade = grade

    def __eq__(self, other):
        return self.grade == other.grade

    def __lt__(self, other):
        return self.grade < other.grade


student1 = Student("Alice", 85)
student2 = Student("Bob", 75)
student3 = Student("Charlie", 85)

print(student1 < student2)  # False
print(student1 > student2)  # True
print(student1 == student3)  # True
print(student1 <= student3) # True
print(student3 >= student2) # True

如下面的代码所示,在 Student 类中没有定义 ge__、__gtle 办法。然而,感激 @total_ordering 装璜器,咱们在不同实例之间的比拟后果都是正确的。

这个装璜器的益处是不言而喻的:

  • 它能够使您的代码更清晰并节俭您的工夫。因为你不须要写所有的比拟办法。
  • 一些旧类可能没有定义足够的比拟办法。将 @total_ordering 装璜器增加到它以供进一步应用会更平安。

3. @contextmanager

Python 有一个上下文管理器机制来帮忙你正确地治理资源。

with open("test.txt",'w') as f:
    f.write("Yang is writing!")

如下面的代码所示,咱们能够应用 with 语句关上一个文件,这样它会在写入后主动敞开。咱们不须要显式调用 f.close() 函数来敞开文件。

有时,咱们须要为一些非凡的需要定义一个自定义的上下文管理器。在这种状况下,@contextmanager 装璜器是咱们的抉择。

例如,上面的代码实现了一个简略的自定义上下文管理器,它能够在文件关上或敞开时打印相应的信息。

from contextlib import contextmanager

@contextmanager
def file_manager(filename, mode):
    print("The file is opening...")
    file = open(filename,mode)
    yield file
    print("The file is closing...")
    file.close()

with file_manager('test.txt', 'w') as f:
    f.write('Yang is writing!')
# The file is opening...
# The file is closing...

4. @property

Getter 和 setter 是面向对象编程 (OOP) 中的重要概念。对于类的每个实例变量,getter 办法返回它的值,而 setter 办法设置或更新它的值。鉴于此,getter 和 setter 也别离称为拜访器和修改器。它们用于爱护您的数据不被间接和意外地拜访或批改。不同的 OOP 语言有不同的机制来定义 getter 和 setter。在 Python 中,咱们能够简略地应用 @property 装璜器。

class Student:
    def __init__(self):
        self._score = 0

    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, s):
        if 0 <= s <= 100:
            self._score = s
        else:
            raise ValueError('The score must be between 0 ~ 100!')

Yang = Student()

Yang.score=99
print(Yang.score)
# 99

Yang.score = 999
# ValueError: The score must be between 0 ~ 100!

如上例所示,score 变量不能设置为 999,这是一个无意义的数字。因为咱们应用 @property 装璜器在 setter 函数中限度了它的可承受范畴。

毫无疑问,增加这个 setter 能够胜利防止意外的谬误或后果。

5. @cached_property

Python 3.8 为 functool 模块引入了一个新的弱小装璜器——@cached_property。它能够将一个类的办法转换为一个属性,该属性的值计算一次,而后在实例的生命周期内作为一般属性缓存。

from functools import cached_property


class Circle:
    def __init__(self, radius):
        self.radius = radius

    @cached_property
    def area(self):
        return 3.14 * self.radius ** 2


circle = Circle(10)
print(circle.area)
# prints 314.0
print(circle.area)
# returns the cached result (314.0) directly

在下面的代码中,咱们通过 @cached_property 润饰了 area 办法。所以没有对同一个不变实例的 circle.area 进行反复计算。

6. @classmethod

在 Python 类中,有 3 种可能的办法类型:

  • 实例办法:绑定到实例的办法。他们能够拜访和批改实例数据。在类的实例上调用实例办法,它能够通过 self 参数拜访实例数据。
  • 类办法:绑定到类的办法。他们不能批改实例数据。在类自身上调用类办法,它接管类作为第一个参数,通常命名为 cls。
  • 静态方法:未绑定到实例或类的办法。

实例办法能够定义为一般的 Python 函数,只有它的第一个参数是 self。然而,要定义一个类办法,咱们须要应用 @classmethod 装璜器。

为了演示,以下示例定义了一个类办法,可用于通过直径获取 Circle 实例:

class Circle:
    def __init__(self, radius):
        self.radius = radius

    @classmethod
    def from_diameter(cls, diameter):
        return cls(diameter / 2)

    @property
    def diameter(self):
        return self.radius * 2

    @diameter.setter
    def diameter(self, diameter):
        self.radius = diameter / 2


c = Circle.from_diameter(8)
print(c.radius)  # 4.0
print(c.diameter)  # 8.0

7. @staticmethod

如前所述,静态方法不绑定到实例或类。它们被蕴含在一个类中只是因为它们在逻辑上属于那个类。

静态方法通常用于执行一组相干工作(例如数学计算)的实用程序类。通过将相干函数组织到类中的静态方法中,咱们的代码将变得更有条理,也更容易了解。

要定义一个静态方法,咱们只须要应用 @staticmethod 装璜器。让咱们看一个例子:

class Student:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name
        self.nickname = None

    def set_nickname(self, name):
        self.nickname = name

    @staticmethod
    def suitable_age(age):
        return 6 <= age <= 70


print(Student.suitable_age(99)) # False
print(Student.suitable_age(27)) # True
print(Student('yang', 'zhou').suitable_age(27)) # True

8. @dataclass

@dataclass 装璜器(Python 3.7 引入)能够主动为一个类生成几个非凡的办法,如__init__、__repr__、__eq__、__lt__等。

因而,它能够为咱们节俭大量编写这些根本办法的工夫。如果一个类次要用于存储数据,那么 @dataclass 装璜器是最好的抉择。

为了演示,上面的示例只定义了一个名为 Point 的类的两个数据字段。感激 @dataclass 装璜器,它足以被应用:

from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float

point = Point(1.0, 2.0)
print(point)
# Point(x=1.0, y=2.0)

9. @atexit.register

来自 atexit 模块的 @register 装璜器能够让咱们在 Python 解释器退出时执行一个函数。

这个装璜器对于执行最终工作十分有用,例如开释资源或只是说再见!

import atexit

@atexit.register
def goodbye():
    print("Bye bye!")

print("Hello Yang!")
  • 输入是:
Hello Yang!
Bye bye!

如示例所示,因为应用了 @register 装璜器,终端打印了“Bye bye!”即便咱们没有显式调用再见函数。

本文由 mdnice 多平台公布

退出移动版