Python教程-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<y):
x,y = y,x
return func(x,y)
# here, we are returning a function with some passed parameters
return inner
divide1 = outer_div(divide)
divide1(2,4)
输出:
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<y):
x,y = y,x
return func(x,y) # here, we are returning the function with the parameters
return inner
# Here, the below is the syntax of generator
@outer_div
def divide(x,y): # here, we are creating a function and passing the parameter
print(x/y)
输出:
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}")
输出:
javatiku
javatiku
javatiku
javatiku
javatiku
在上面的示例中,@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()方法存储对函数的引用,并可以进行任何其他所需的初始化。