Python 程式語言的一大優點是它把所有功能都打包到一個小套件中,這些功能非常有用。
許多特性可以完全改變 Python 程式碼的功能,這使得語言更加靈活。如果使用得當,其中一些功能可以有效縮短編寫程式所需的時間。
實現這些目標的一個很好的例子是 Python 的裝飾器。
裝飾器(decorators)是可以用來改變一個 Python 函數物件行為的函數。它們可以應用於類別和函數,可以做很多非常有趣的事情!
裝飾器可以用來縮短程式碼、加速程式碼並徹底改變程式碼在 Python 中的行為方式。
不用說,這當然可以派上用場!今天我想炫耀一些我認為值得一試的裝飾器。
有很多裝飾器,但我選擇了一些我認為具有最酷功能的裝飾器。
此清單中的第一個裝飾器來自 functools 模組。
該模組包含在標準庫中,非常易於使用。它還包含比這個裝飾器更酷的功能,但這個裝飾器肯定是我最喜歡的。
此裝飾器可用於使用快取加速函數的連續運作。當然,這應該在使用時記住一些關於快取的注意事項,但在通用使用情況下,大多數時候這個裝飾器是值得使用的。
能夠用一個簡單的裝飾器來加速程式碼是非常棒的。
可以從這樣的裝飾器中受益的函數的一個很好的例子是遞歸函數,例如計算階乘的函數:
def factorial(n): return n * factorial(n-1) if n else 1
遞歸在計算時間上可能非常困難,但添加此裝飾器有助於顯著加快此函數的連續運行速度。
@lru_cache def factorial(n): return n * factorial(n-1) if n else 1
現在每當我們執行這個函數時,前幾個階乘計算就會被儲存到快取中。
因此,下次我們呼叫函數時,我們只需要計算我們之前使用的階乘之後的階乘。
當然,並不是所有的階乘計算都會被保存,但是很容易理解為什麼這個裝飾器的一個很好的應用程式來加速一些自然很慢的程式碼。
JIT 是即時編譯(Just In Time)的縮寫。通常每當我們在 Python 中執行一些程式碼時,發生的第一件事就是編譯。
這種編譯會產生一些開銷,因為類型被分配了內存,並儲存為未分配但已命名的別名。使用即時編譯,我們在執行時才會進行編譯。
在很多方面,我們可以將其視為類似於平行計算的東西,其中 Python 解釋器同時處理兩件事以節省一些時間。
Numba JIT 編譯器以將此概念提供到 Python 中而聞名。與@lru_cache 類似,可以非常輕鬆地呼叫此裝飾器,並立即提高程式碼的效能。 Numba 套件提供了 jit 裝飾器,它使運行更密集的軟體變得更加容易,而不必進入 C。
以下案例使用@jit 裝飾器加速蒙特卡羅方法計算。
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
do_twice 裝飾器的功能與它的名字差不多。此裝飾器可用於透過一次呼叫來運行兩次函數。這當然有一些用途,我發現它對調試特別有用。
它可以用來測量兩個不同迭代的效能。以 Functools 為例,我們可以讓一個函數執行兩次,以檢查是否有改進。該函數由 Python 中的裝飾器模組提供,該模組位於標準庫中。
from decorators import do_twice @do_twice def timerfunc(): %timeit factorial(15)
count_calls 裝飾器可用於提供有關函數在軟體中使用多少次的資訊。
就像 do_twice 一樣,這當然可以在偵錯時派上用場。
當新增到給定的函數時,我們將收到一個輸出,告訴我們該函數每次運行時已經運行了多少次。這個裝飾器也在標準庫的裝飾器模組中。
from decorators import count_calls @count_calls def function_example(): print("Hello World!") function_example() function_example() function_example()
為了節省編寫類別的時間,我一直使用的最好的裝飾器之一是@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)
這段程式碼將自動建立一個初始化函數 init(),其中包含填充類別中資料所需的位置參數。
它們也將自動提供給 self,因此無需編寫一個很長的函數來將一些資料參數放入類別中。
為了理解單例裝飾器的用途,我們首先需要了解單例(singleton)是什麼。從某種意義上說,單例是全域變數類型的一個版本。
这意味着类型被定义为只存在一次。尽管这些在 C++ 等语言中很常见,但在 Python 中却很少见到。使用单例,我们可以创建一个只使用一次的类并改变类,而不是通过初始化来构造新的类型。
通常,单例装饰器是由用户自己编写的,实际上并不是导入的。
这是因为单例仍然是对我们单例装饰器中提供的模板的引用。我们可以命名一个单例函数并编写一个包装器,以便在我们的类上使用这个装饰器:
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):
另一种方法是使用元类!
在科学计算中经常派上用场的一种装饰器是 @use_unit 装饰器。
此装饰器可用于更改返回结果的表示单位。这对于那些不想在数据中添加度量单位但仍希望人们知道这些单位是什么的人很有用。
这个装饰器也不是在任何模块中真正可用,但它是非常常见的,对科学应用程序非常有用。
def use_unit(unit): """Have a function return a Quantity with given 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
Functools 凭借非常有用的@singledispatch 装饰器再次在此列表中脱颖而出。
单调度是一种编程技术,在许多编程语言中都很常见,因为它是一种非常棒的编程方式。虽然我更喜欢多调度,但我认为单调度可以在很多方面扮演相同的角色。
这个装饰器使得在 Python 中使用多类型数据变得更加容易, 尤其当我们希望通过同一方法传递多种类型数据时,情况更是如此。
@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)
以上是超棒!推薦八個酷炫的 Python 裝飾器!的詳細內容。更多資訊請關注PHP中文網其他相關文章!