Rumah > pembangunan bahagian belakang > Tutorial Python > Penghias Python ditaip sepenuhnya dengan parameter

Penghias Python ditaip sepenuhnya dengan parameter

WBOY
Lepaskan: 2023-04-13 17:58:06
ke hadapan
1392 orang telah melayarinya

Penghias Python ditaip sepenuhnya dengan parameter

Kod yang ditunjukkan dalam artikel pendek ini diambil daripada projek sumber terbuka kecil saya yang direka oleh kontrak, yang menyediakan penghias bertaip. Penghias adalah konsep yang sangat berguna dan anda pasti akan menemui banyak tentangnya dalam talian. Ringkasnya, mereka membenarkan kod dilaksanakan setiap kali (sebelum dan selepas) fungsi yang dihias dipanggil. Dengan cara ini anda boleh mengubah suai parameter fungsi atau mengembalikan nilai, mengukur masa pelaksanaan, menambah pengelogan, melakukan pemeriksaan jenis masa pelaksanaan dan banyak lagi. Ambil perhatian bahawa penghias juga boleh ditulis untuk kelas, menyediakan pendekatan metaprogramming yang lain (seperti yang dilakukan dalam pakej attrs)

Dalam bentuk yang paling mudah, definisi penghias kelihatan seperti kod berikut:

def my_first_decorator(func):
 def wrapped(*args, **kwargs):
 # do something before
 result = func(*args, **kwargs)
 # do something after
 return result
 return wrapped
@my_first_decorator
def func(a):
 return a
Salin selepas log masuk

Kod di atas, kerana apabila fungsi bersarang yang dibalut ditakrifkan, pembolehubah sekelilingnya boleh diakses dalam fungsi dan disimpan dalam memori, selagi fungsi itu digunakan di suatu tempat (Ini dipanggil penutupan dalam bahasa pengaturcaraan berfungsi).

Sangat mudah, tetapi ini mempunyai beberapa kelemahan. Masalah terbesar ialah fungsi yang diubah suai akan kehilangan nama fungsi sebelumnya (anda boleh melihat ini dengan inspect.signature), docstringnya, dan juga namanya Ini adalah masalah dengan alat dokumentasi kod sumber (seperti sphinx), Tetapi ia boleh diselesaikan dengan mudah menggunakan penghias functools.wraps dalam pustaka standard:

from functools import wraps
from typing import Any, Callable, TypeVar, ParamSpec
P = ParamSpec("P") # 需要python >= 3.10
R = TypeVar("R")
def my_second_decorator(func: Callable[P, R]) -> Callable[P, R]:
 @wraps(func)
 def wrapped(*args: Any, **kwargs: Any) -> R:
 # do something before
 result = func(*args, **kwargs)
 # do something after
 return result
 return wrapped
@my_second_decorator
def func2(a: int) -> int:
 """Does nothing"""
 return a
print(func2.__name__)
# 'func2'
print(func2.__doc__)
# 'Does nothing'
Salin selepas log masuk

Dalam contoh ini, saya telah menambah anotasi jenis dan petunjuk jenis diperlukan untuk Python tambahan penting untuk dibuat. Kebolehbacaan yang lebih baik, penyiapan kod dalam IDE, dan kebolehselenggaraan asas kod yang lebih besar hanyalah beberapa contoh. Kod di atas sepatutnya merangkumi kebanyakan kes penggunaan, tetapi penghias tidak boleh diparameterkan. Pertimbangkan untuk menulis penghias yang merekodkan masa pelaksanaan fungsi, tetapi hanya jika ia melebihi beberapa saat tertentu. Nombor ini harus dikonfigurasikan secara individu untuk setiap fungsi yang dihias. Jika tidak dinyatakan, nilai lalai harus digunakan dan penghias hendaklah digunakan tanpa kurungan untuk memudahkan penggunaan:

@time(threshold=2)
def func1(a):
...
# No paranthesis when using default threshold
@time
def func2(b):
...
Salin selepas log masuk

jika anda boleh menggunakannya dalam kurungan kes kedua , atau tidak memberikan nilai lalai untuk parameter sama sekali, maka resipi ini akan mencukupi:

from functools import wraps
from typing import Any, Callable, TypeVar, ParamSpec
P = ParamSpec("P") # 需要python >= 3.10
R = TypeVar("R")
def my_third_decorator(threshold: int = 1) -> Callable[[Callable[P, R]], Callable[P, R]]:
 def decorator(func: Callable[P, R]) -> Callable[P, R]:
 @wraps(func)
 def wrapper(*args: Any, **kwargs: Any) -> R:
 # do something before you can use `threshold`
 result = func(*args, **kwargs)
 # do something after
 return result
 return wrapper
 return decorator
@my_third_decorator(threshold=2)
def func3a(a: int) -> None:
...
# works
@my_third_decorator()
def func3b(a: int) -> None:
...
# Does not work!
@my_third_decorator
def func3c(a: int) -> None:
...
Salin selepas log masuk

Untuk menampung kes ketiga, terdapat pakej, iaitu pembalut dan penghias, yang sebenarnya boleh melakukan lebih daripada cuma Hanya tambah parameter pilihan. Walaupun kualitinya sangat tinggi, mereka memperkenalkan sedikit kerumitan tambahan. Menggunakan fungsi yang dihias bungkus, saya terus menghadapi masalah bersiri apabila menjalankan fungsi pada kelompok jauh. Setahu saya, kedua-duanya tidak ditaip sepenuhnya, jadi penyemak/linters jenis statik (seperti mypy) gagal dalam mod ketat.

Saya terpaksa menyelesaikan masalah ini apabila saya bekerja pada pakej saya sendiri dan memutuskan untuk menulis penyelesaian saya sendiri. Ia menjadi corak yang mudah digunakan semula tetapi sukar untuk ditukar menjadi perpustakaan.

Ia menggunakan penghias yang terlebih muatan daripada perpustakaan standard. Dengan cara ini, penghias yang sama boleh ditentukan untuk digunakan dengan penghias tanpa parameter kami. Selain daripada itu, ia adalah gabungan dua coretan di atas. Satu kelemahan pendekatan ini ialah semua parameter perlu diberikan sebagai hujah kata kunci (ini meningkatkan kebolehbacaan selepas semua)

from typing import Callable, TypeVar, ParamSpec
from functools import partial, wraps
P = ParamSpec("P") # requires python >= 3.10
R = TypeVar("R
@overload
def typed_decorator(func: Callable[P, R]) -> Callable[P, R]:
...
@overload
def typed_decorator(*, first: str = "x", second: bool = True) -> Callable[[Callable[P, R]], Callable[P, R]]:
...
def typed_decorator(
 func: Optional[Callable[P, R]] = None, *, first: str = "x", second: bool = True
) -> Union[Callable[[Callable[P, R]], Callable[P, R]], Callable[P, R]]:
 """
Describe what the decorator is supposed to do!
Parameters
----------
first : str, optional
First argument, by default "x".
This is a keyword-only argument!
second : bool, optional
Second argument, by default True.
This is a keyword-only argument!
"""
 def wrapper(func: Callable[P, R], *args: Any, **kw: Any) -> R:
 """The actual logic"""
 # Do something with first and second and produce a `result` of type `R`
 return result
 # Without arguments `func` is passed directly to the decorator
 if func is not None:
 if not callable(func):
 raise TypeError("Not a callable. Did you use a non-keyword argument?")
 return wraps(func)(partial(wrapper, func))
 # With arguments, we need to return a function that accepts the function
 def decorator(func: Callable[P, R]) -> Callable[P, R]:
 return wraps(func)(partial(wrapper, func))
 return decorator
Salin selepas log masuk

Kemudian, kami boleh menggunakan Penghias kami tanpa parameter

@typed_decorator
def spam(a: int) -> int:
 return a
@typed_decorator(first = "y
def eggs(a: int) -> int:
 return a
Salin selepas log masuk

Corak ini pasti mempunyai sedikit overhed, tetapi faedahnya melebihi kos.

Teks asal: ​​https://www.php.cn/link/d0f82e1046ccbd597c7f2a7bfba9e7dd​

Atas ialah kandungan terperinci Penghias Python ditaip sepenuhnya dengan parameter. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Label berkaitan:
sumber:51cto.com
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan