📅  最后修改于: 2020-10-28 01:24:43             🧑  作者: Mango
装饰器是Python最有用,功能最强大的工具之一。这些用于修改函数的行为。装饰器提供了包装另一个函数的灵活性,以扩展被包装函数的工作,而无需对其进行永久性修改。
“在装饰器中,将函数作为参数传递给另一个函数,然后在包装函数内部函数。”
也称为元编程,其中程序的一部分尝试在编译时更改程序的另一部分。
在了解Decorator之前,我们需要了解Python的一些重要概念。
Python具有最有趣的功能,即使类或我们在Python定义的任何变量也被视为对象,所有东西都被视为对象。函数是Python的一流对象,因为它们可以引用,传递给变量并从其他函数返回。示例如下:
def func1(msg):
print(msg)
func1("Hii")
func2 = func1
func2("Hii")
输出:
Hii
Hii
在上面的程序中,当我们运行代码时,两个函数都给出相同的输出。 func2引用函数func1并充当函数。我们需要了解以下函数的概念:
Python提供了在另一个函数定义函数的功能。这些类型的功能称为内部功能。考虑以下示例:
def func():
print("We are in first function")
def func1():
print("This is first child function")
def func2():
print(" This is second child function")
func1()
func2()
func()
输出:
We are in first function
This is first child function
This is second child function
在上面的程序中,子函数的声明方式无关紧要。子函数的执行会影响输出。这些子函数在本地受func()限制,因此不能单独调用它们。
接受其他函数作为参数的函数也被称为高阶函数。考虑以下示例:
def add(x):
return x+1
def sub(x):
return x-1
def operator(func, x):
temp = func(x)
return temp
print(operator(sub,10))
print(operator(add,20))
输出:
9
21
在上面的程序中,我们已将dec()函数和inc()函数作为运算符()函数的参数传递了。
一个函数可以返回另一个函数。考虑以下示例:
def hello():
def hi():
print("Hello")
return hi
new = hello()
new()
输出:
Hello
在上面的程序中,hi()函数嵌套在hello()函数内部。每当我们调用hi()时,它将返回。
让我们举一个例子来了解参数化装饰器函数:
def divide(x,y):
print(x/y)
def outer_div(func):
def inner(x,y):
if(x
输出:
2.0
在上面的程序中,我们装饰了out_div()有点笨重。代替使用上述方法, Python允许通过@symbol轻松使用装饰器。有时它称为“派”语法。
def outer_div(func):
def inner(x,y):
if(x
输出:
2.0
我们也可以通过调用该装饰器函数来重用该装饰器。让我们将装饰器制作成可以在许多其他功能中使用的模块。使用以下代码创建一个名为mod_decorator.py的文件:
def do_twice(func):
def wrapper_do_twice():
func()
func()
return wrapper_do_twice
我们可以在其他文件中导入mod_decorator.py。
from decorator import do_twice
@do_twice
def say_hello():
print("Hello There")
say_hello()
输出:
Hello There
Hello There
我们要在函数传递一些参数。让我们在以下代码中进行操作:
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通常用于修改类属性的获取器和设置器。让我们通过以下示例了解它:
示例:1- @property decorator-通过使用它,我们可以将类函数用作属性。考虑以下代码:
class 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:", stu.name)
print("Grade:", stu.grade)
print(stu.display)
输出:
Name: John
Grade: B
John got grade B
示例:2-@staticmethod装饰器-@staticmethod用于在类中定义静态方法。通过使用类名以及该类的实例来调用它。考虑以下代码:
class Person:
@staticmethod
def 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
def repeat(num):
#Creating and returning a wrapper function
def decorator_repeat(func):
@functools.wraps(func)
def wrapper(*args,**kwargs):
for _ in range(num):
value = func(*args,**kwargs)
return value
return wrapper
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
def count_function(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
@count_function
def say_hello():
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
class Count_Calls:
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():
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 __()方法存储对该函数的引用,并且可以执行任何其他所需的初始化。