Python装饰器
装饰器是Python中最有帮助和强大的工具之一。它们用于修改函数的行为。装饰器提供了灵活性,可以包装另一个函数以扩展包装函数的工作,而不会永久修改它。
在装饰器中,函数作为参数传递到另一个函数中,然后在包装器函数内部调用。
它也被称为 元编程 ,即程序的一部分试图在编译时更改另一部分程序。
在理解 装饰器 之前,我们需要了解Python的一些重要概念。
Python中的函数是什么
Python具有最有趣的特性,即一切都被视为对象,甚至是类或我们在Python中定义的任何变量也被视为对象。函数是Python中的 一等公民 对象,因为它们可以引用、传递给变量并从其他函数返回。例如:
示例:
def func1(msg): # here, we are creating a function and passing the parameter
print(msg)
func1("Hii, welcome to function ") # Here, we are printing the data of function 1
func2 = func1 # Here, we are copying the function 1 data to function 2
func2("Hii, welcome to function ") # Here, we are printing the data of function 2
输出:
Hii, welcome to function
Hii, welcome to function
在上述程序中,当我们运行代码时,两个函数的输出都相同。 func2 引用函数 func1 并充当函数的角色。我们需要了解以下函数的概念:
- 函数可以被引用,并且可以作为变量传递和从其他函数返回。
- 函数可以在另一个函数中声明,并作为参数传递给另一个函数。
内部函数
Python提供了在另一个函数内定义函数的功能。这些类型的函数被称为内部函数。考虑以下示例:
示例:
def func(): # here, we are creating a function and passing the parameter
print("We are in first function") # Here, we are printing the data of function
def func1(): # here, we are creating a function and passing the parameter
print("This is first child function") # Here, we are printing the data of function 1
def func2(): # here, we are creating a function and passing the parameter
print("This is second child function") # Here, we are printing the data of # function 2
func1()
func2()
func()
输出:
We are in first function
This is first child function
This is second child function
在上面的程序中,子函数的声明方式并不重要。子函数的执行会对输出产生影响。这些子函数与 func() 局部绑定,因此不能被单独调用。
接受其他函数作为参数的函数也被称为 高阶函数 。考虑以下示例:
示例:
def add(x): # here, we are creating a function add and passing the parameter
return x+1 # here, we are returning the passed value by adding 1
def sub(x): # here, we are creating a function sub and passing the parameter
return x-1 # here, we are returning the passed value by subtracting 1
def operator(func, x): # here, we are creating a function and passing the parameter
temp = func(x)
return temp
print(operator(sub,10)) # here, we are printing the operation subtraction with 10
print(operator(add,20)) # here, we are printing the operation addition with 20
输出:
9
21
在上面的程序中,我们将 sub() 函数和 add() 函数作为参数传递给 operator() 函数。
一个函数可以返回另一个函数。考虑下面的示例:
示例:
def hello(): # here, we are creating a function named hello
def hi(): # here, we are creating a function named hi
print("Hello") # here, we are printing the output of the function
return hi # here, we are returning the output of the function
new = hello()
new()
输出:
Hello
在上面的程序中, hi() 函数嵌套在 hello() 函数内部。每次调用 hi() 时,它都会返回。
使用参数装饰函数
让我们举一个示例来理解带参数的装饰函数:
示例:
def divide(x,y): # here, we are creating a function and passing the parameter
print(x/y) # Here, we are printing the result of the expression
def outer_div(func): # here, we are creating a function and passing the parameter
def inner(x,y): # here, we are creating a function and passing the parameter
if(x
输出:
2.0
语法修饰器
在上面的程序中,我们装饰了 out_div() 这个有点臃肿的函数。Python允许使用装饰器的简便方法,使用 @符号 。有时候也被称为”pie”语法。
def outer_div(func): # here, we are creating a function and passing the parameter
def inner(x,y): # here, we are creating a function and passing the parameter
if(x
输出:
2.0
重复使用装饰器
我们也可以通过调用装饰器函数来重复使用装饰器。让我们将装饰器制作成一个独立的模块,可以在许多其他函数中使用。创建一个名为 mod_decorator.py 的文件,代码如下:
def do_twice(func): # here, we are creating a function and passing the parameter
def wrapper_do_twice():
# here, we are creating a function and passing the parameter
func()
func()
return wrapper_do_twice
We can import mod_decorator.py in another file.
from decorator import do_twice
@do_twice
def say_hello():
print("Hello There")
say_hello()
我们可以在其他文件中导入mod_decorator.py。
from decorator import do_twice
@do_twice
def say_hello():
print("Hello There")
say_hello()
输出:
Hello There
Hello There
Python带参数的装饰器
我们想在函数中传递一些参数。让我们在以下代码中完成它:
from decorator import do_twice
@do_twice
def display(name):
print(f"Hello {name}")
display()
输出:
TypeError: display() missing 1 required positional argument: 'name'
如我们所见,这个函数没有接受参数。运行这段代码会产生一个错误。我们可以通过在内部装饰函数中使用 *args 和 ****kwargs** 来修复这个错误。修改 decorator.py 如下:
def do_twice(func):
def wrapper_function(*args,**kwargs):
func(*args,**kwargs)
func(*args,**kwargs)
return wrapper_function
现在 wrapper_function() 可以接受任意数量的参数并将它们传递给函数。
from decorator import do_twice
@do_twice
def display(name):
print(f"Hello {name}")
display("John")
输出:
Hello John
Hello John
从装饰函数中返回值
我们可以控制装饰函数的返回类型。下面是一个示例:
from decorator import do_twice
@do_twice
def return_greeting(name):
print("We are created greeting")
return f"Hi {name}"
hi_adam = return_greeting("Adam")
输出:
We are created greeting
We are created greeting
精美的装饰器
让我们通过以下主题来理解精美的装饰器:
类装饰器
Python提供了两种装饰类的方式。首先,我们可以装饰类内部的方法;Python中有内置装饰器,如 @classmethod、@staticmethod 和 @property 。@classmethod和@staticmethod定义了与类的任何其他实例无关的类内方法。@property通常用于修改类属性的getter和setter。我们通过以下示例来理解它:
示例:1-
@property装饰器 - 使用它,我们可以将类函数作为属性使用。考虑以下代码:
class Student: # here, we are creating a class with the name Student
def __init__(self,name,grade):
self.name = name
self.grade = grade
@property
def display(self):
return self.name + " got grade " + self.grade
stu = Student("John","B")
print("Name of the student: ", stu.name)
print("Grade of the student: ", stu.grade)
print(stu.display)
输出:
Name of the student: John
Grade of the student: B
John got grade B
示例:2-
@staticmethod装饰器 - @staticmethod用于在类中定义静态方法。它可以通过使用类名以及类的实例来调用。考虑以下代码:
class Person: # here, we are creating a class with the name Student
@staticmethod
def hello(): # here, we are defining a function hello
print("Hello Peter")
per = Person()
per.hello()
Person.hello()
输出:
Hello Peter
Hello Peter
单例类
单例类只有一个实例。Python中有许多单例类,包括True、None等。
嵌套装饰器
我们可以通过将装饰器叠加在一起来使用多个装饰器。让我们看下面的示例:
@function1
@function2
def function(name):
print(f "{name}")
在上面的代码中,我们使用了嵌套装饰器,将它们叠加在一起。
带有参数的装饰器
在装饰器中传递参数总是很有用的。根据参数的给定值,可以多次执行装饰器。让我们考虑以下示例:
示例:
Import functools # here, we are importing the functools into our program
def repeat(num): # here, we are defining a function repeat and passing parameter
# Here, we are creating and returning a wrapper function
def decorator_repeat(func):
@functools.wraps(func)
def wrapper(*args,**kwargs):
for _ in range(num): # here, we are initializing a for loop and iterating till num
value = func(*args,**kwargs)
return value # here, we are returning the value
return wrapper # here, we are returning the wrapper class
return decorator_repeat
#Here we are passing num as an argument which repeats the print function
@repeat(num=5)
def function1(name):
print(f"{name}")
输出:
JavatPoint
JavatPoint
JavatPoint
JavatPoint
JavatPoint
在上面的示例中, @repeat 是指一个可以在另一个函数中调用的函数对象。 @repeat(num = 5) 会返回一个作为装饰器的函数。
上面的代码可能看起来很复杂,但这是最常用的装饰器模式,我们使用了一个额外的 def 来处理装饰器的参数。
注意:带有参数的装饰器在编程中并不经常使用,但它提供了灵活性。我们可以在使用时带有或不带参数。
有状态的装饰器
有状态的装饰器用于跟踪装饰器的状态。让我们考虑一个示例,我们正在创建一个计算函数被调用次数的装饰器。
示例:
Import functools # here, we are importing the functools into our program
def count_function(func):
# here, we are defining a function and passing the parameter func
@functools.wraps(func)
def wrapper_count_calls(*args, **kwargs):
wrapper_count_calls.num_calls += 1
print(f"Call{wrapper_count_calls.num_calls} of {func.__name__!r}")
return func(*args, **kwargs)
wrapper_count_calls.num_calls = 0
return wrapper_count_calls # here, we are returning the wrapper call counts
@count_function
def say_hello(): # here, we are defining a function and passing the parameter
print("Say Hello")
say_hello()
say_hello()
输出:
Call 1 of 'say_hello'
Say Hello
Call 2 of 'say_hello'
Say Hello
在上面的程序中,状态表示存储在包装函数的 .num_calls 中的函数调用次数。当我们调用 say_hello() 时,它会显示函数调用的次数。
类作为装饰器
类是维护状态的最佳方式。在本节中,我们将学习如何将类作为装饰器使用。在这里,我们将创建一个包含 __init__()
并将 func 作为参数的类。该类需要是可调用的,以便可以替代被装饰的函数。
为了使类可调用,我们实现了特殊的 __call__()
方法。
代码
import functools # here, we are importing the functools into our program
class Count_Calls: # here, we are creating a class for getting the call count
def __init__(self, func):
functools.update_wrapper(self, func)
self.func = func
self.num_calls = 0
def __call__(self, *args, **kwargs):
self.num_calls += 1
print(f"Call{self.num_calls} of {self.func.__name__!r}")
return self.func(*args, **kwargs)
@Count_Calls
def say_hello(): # here, we are defining a function and passing the parameter
print("Say Hello")
say_hello()
say_hello()
say_hello()
输出:
Call 1 of 'say_hello'
Say Hello
Call 2 of 'say_hello'
Say Hello
Call 3 of 'say_hello'
Say Hello
构造函数 __init__()
存储函数的引用,并可以进行任何其他所需的初始化。