Heim  >  Artikel  >  Backend-Entwicklung  >  Python-Dekoratoren verstehen und anwenden

Python-Dekoratoren verstehen und anwenden

WBOY
WBOYnach vorne
2023-05-08 08:10:101399Durchsuche

Decorator ist eine erweiterte Python-Syntax. Es kann eine Funktion, Methode oder Klasse verarbeitet werden. In Python verfügen wir über mehrere Methoden zur Verarbeitung von Funktionen und Klassen. Im Vergleich zu anderen Methoden verfügen Dekoratoren über eine einfache Syntax und eine hohe Lesbarkeit des Codes. Daher werden Dekoratoren häufig in Python-Projekten verwendet. Dekoratoren werden häufig in Szenarien mit übergreifenden Anforderungen verwendet. Zu den klassischen Beispielen gehören Einfügungsprotokolle, Leistungstests, Transaktionsverarbeitung, Webberechtigungsüberprüfung, Cache usw.

如何理解 Python 装饰器

Der Vorteil des Dekorators besteht darin, dass er denselben Code in einer großen Anzahl von Funktionen extrahieren kann, die nichts enthalten mit der Funktion selbst zu tun und die Wiederverwendung fortzusetzen. Das heißt, Funktionen können in völlig unterschiedliche Verhaltensweisen „modifiziert“ werden und Geschäftslogik kann effektiv orthogonal zerlegt werden. Kurz gesagt besteht der Zweck eines Dekorateurs darin, einem vorhandenen Objekt zusätzliche Funktionalität hinzuzufügen. Beispielsweise erfordert die Protokollierung die Protokollierung bestimmter Funktionen. Der dumme Weg besteht darin, jeder Funktion Code hinzuzufügen. Wenn sich der Code ändert, ist das tragisch. Die Dekoratormethode besteht darin, einen speziellen Protokollierungsdekorator zu definieren, um die erforderlichen Funktionen zu dekorieren.

如何理解 Python 装饰器

Pythons Decorator ist der verwendeten Annotation von Java/C# sehr ähnlich. Beide fügen vor dem Methodennamen eine @XXX-Annotation ein, um die Methode zu kommentieren. Etwas dekorieren. Allerdings ist Annotation von Java/C# auch sehr entmutigend. Bevor Sie es verwenden, müssen Sie eine Reihe von Dokumenten der Annotation-Klassenbibliothek verstehen, was Ihnen das Gefühl gibt, eine andere Sprache zu lernen. Python verwendet eine sehr elegante Methode im Vergleich zu Decorator Pattern und Annotation. Diese Methode erfordert keine Beherrschung komplexer OO-Modelle oder verschiedener Klassenbibliotheksregeln von Annotation. Sie wird vollständig auf der Sprachebene gespielt: 1. funktionale Programmiertechniken.

Das Prinzip hinter Dekoratoren

In Python ist die Implementierung von Dekoratoren sehr praktisch. Der Grund ist: Funktionen können herumgeworfen werden.

Pythons Funktionen sind Objekte

Um Dekoratoren zu verstehen, müssen Sie zunächst wissen, dass Funktionen in Python Objekte sind. Es ist wichtig, dies zu verstehen. Sehen wir uns anhand eines Beispiels an, warum.

def shout(word="yes"):

**return** word.capitalize() + "!"

print(shout())

# outputs : 'Yes!'

# 作为一个对象,你可以像其他对象一样把函数赋值给其他变量

scream = shout

# 注意我们没有用括号:我们不是在调用函数,

# 而是把函数'shout'的值绑定到'scream'这个变量上

# 这也意味着你可以通过'scream'这个变量来调用'shout'函数

print(scream())

# outputs : 'Yes!'

# 不仅如此,这也还意味着你可以把原来的名字'shout'删掉,

# 而这个函数仍然可以通过'scream'来访问

del shout

**try**:

print(shout())

**except** NameError as e:

print(e)

# outputs: "name 'shout' is not defined"

print(scream())

# outputs: 'Yes!'

Ein weiteres interessantes Merkmal von Python-Funktionen ist, dass sie im Hauptteil einer anderen Funktion definiert werden können.

def talk():

# 你可以在 'talk' 里动态的(on the fly)定义一个函数...

**def** whisper(word="yes"):

**return** word.lower() + "..."

# ... 然后马上调用它!

print(whisper())

# 每当调用'talk',都会定义一次'whisper',然后'whisper'在'talk'里被调用

talk()

# outputs:

# "yes..."

# 但是"whisper" 在 "talk"外并不存在:

**try**:

print(whisper())

**except** NameError as e:

print(e)

# outputs : "name 'whisper' is not defined"

Funktionsreferenzen

Sie wussten einfach, dass Pythons Funktionen auch Objekte sind, also:

  • Ja, einer Variablen zugewiesen # 🎜🎜#
  • kann in einem anderen Funktionskörper definiert werden
Das bedeutet also, dass eine Funktion eine andere Funktion zurückgeben kann:

def get_talk(type="shout"):

# 我们先动态定义一些函数

**def** shout(word="yes"):

**return** word.capitalize() + "!"

**def** whisper(word="yes"):

**return** word.lower() + "..."

# 然后返回其中一个

**if** type == "shout":

# 注意:我们是在返回函数对象,而不是调用函数,所以不要用到括号 "()"

**return** shout

**else**:

**return** whisper

# 那你改如何使用d呢?

# 先把函数赋值给一个变量

talk = get_talk()

# 你可以发现 "talk" 其实是一个函数对象:

print(talk)

# outputs : <function shout at 0xb7ea817c>

# 这个对象就是 get_talk 函数返回的:

print(talk())

# outputs : Yes!

# 你甚至还可以直接这样使用:

print(get_talk("whisper")())

# outputs : yes...

Da du kannst Geben Sie eine Funktion zurück, Sie können sie wie einen Parameter übergeben:

def shout(word="yes"):

**return** word.capitalize() + "!"

scream = shout

**def** do_something_before(func):

print("I do something before then I call the function you gave me")

print(func())

do_something_before(scream)

# outputs:

# I do something before then I call the function you gave me

# Yes!

Decorator-Praxis

Jetzt verfügen Sie über alle Grundkenntnisse, um Decorators zu verstehen. Dekoratoren sind Verpackungsmaterialien, die es Ihnen ermöglichen, anderen Code vor oder nach der Ausführung der dekorierten Funktion auszuführen, ohne die Funktion selbst zu ändern.

Handgemachter Dekorateur

# 一个装饰器是一个需要另一个函数作为参数的函数

**def** my_shiny_new_decorator(a_function_to_decorate):

# 在装饰器内部动态定义一个函数:wrapper(原意:包装纸).

# 这个函数将被包装在原始函数的四周

# 因此就可以在原始函数之前和之后执行一些代码.

**def** the_wrapper_around_the_original_function():

# 把想要在调用原始函数前运行的代码放这里

print("Before the function runs")

# 调用原始函数(需要带括号)

a_function_to_decorate()

# 把想要在调用原始函数后运行的代码放这里

print("After the function runs")

# 直到现在,"a_function_to_decorate"还没有执行过 (HAS NEVER BEEN EXECUTED).

# 我们把刚刚创建的 wrapper 函数返回.

# wrapper 函数包含了这个函数,还有一些需要提前后之后执行的代码,

# 可以直接使用了(It's ready to use!)

**return** the_wrapper_around_the_original_function

# Now imagine you create a function you don't want to ever touch again.

**def** a_stand_alone_function():

print("I am a stand alone function, don't you dare modify me")

a_stand_alone_function()

# outputs: I am a stand alone function, don't you dare modify me

# 现在,你可以装饰一下来修改它的行为.

# 只要简单的把它传递给装饰器,后者能用任何你想要的代码动态的包装

# 而且返回一个可以直接使用的新函数:

a_stand_alone_function_decorated = my_shiny_new_decorator(a_stand_alone_function)

a_stand_alone_function_decorated()

# outputs:

# Before the function runs

# I am a stand alone function, don't you dare modify me

# After the function runs

Syntaktischer Zucker für Dekorateure

Schreiben wir das vorherige Beispiel mit der Dekorator-Syntax um:# 🎜🎜#
# 一个装饰器是一个需要另一个函数作为参数的函数

**def** my_shiny_new_decorator(a_function_to_decorate):

# 在装饰器内部动态定义一个函数:wrapper(原意:包装纸).

# 这个函数将被包装在原始函数的四周

# 因此就可以在原始函数之前和之后执行一些代码.

**def** the_wrapper_around_the_original_function():

# 把想要在调用原始函数前运行的代码放这里

print("Before the function runs")

# 调用原始函数(需要带括号)

a_function_to_decorate()

# 把想要在调用原始函数后运行的代码放这里

print("After the function runs")

# 直到现在,"a_function_to_decorate"还没有执行过 (HAS NEVER BEEN EXECUTED).

# 我们把刚刚创建的 wrapper 函数返回.

# wrapper 函数包含了这个函数,还有一些需要提前后之后执行的代码,

# 可以直接使用了(It's ready to use!)

**return** the_wrapper_around_the_original_function

@my_shiny_new_decorator

**def** another_stand_alone_function():

print("Leave me alone")

another_stand_alone_function()

# outputs:

# Before the function runs

# Leave me alone

# After the function runs

Ja , das ist es, es ist so einfach. @decorator ist nur die Abkürzung der folgenden Anweisung:

another_stand_alone_function = my_shiny_new_decorator(another_stand_alone_function)

Der syntaktische Dekorator-Zucker ist eigentlich eine pythonische Variante des Dekoratormusters. Um die Entwicklung zu erleichtern, verfügt Python über mehrere integrierte klassische Entwurfsmuster, beispielsweise Iteratoren. Natürlich können Sie Dekoratoren auch stapeln:

def bread(func):

**def** wrapper():

print("</''''''>")

func()

print("<______/>")

**return** wrapper

**def** ingredients(func):

**def** wrapper():

print("#tomatoes#")

func()

print("~salad~")

**return** wrapper

**def** sandwich(food="--ham--"):

print(food)

sandwich()

# outputs: --ham--

sandwich = bread(ingredients(sandwich))

sandwich()

# outputs:

# </''''''>

# #tomatoes#

# --ham--

# ~salad~

# <______/>

Wird in der Dekorator-Syntax von Python verwendet:

def bread(func):

**def** wrapper():

print("</''''''>")

func()

print("<______/>")

**return** wrapper

**def** ingredients(func):

**def** wrapper():

print("#tomatoes#")

func()

print("~salad~")

**return** wrapper

@bread

@ingredients

**def** sandwich(food="--ham--"):

print(food)

sandwich()

# outputs:

# </''''''>

# #tomatoes#

# --ham--

# ~salad~

# <______/>

Die Reihenfolge, in der die Dekoratoren platziert werden, ist ebenfalls wichtig:

def bread(func):

**def** wrapper():

print("</''''''>")

func()

print("<______/>")

**return** wrapper

**def** ingredients(func):

**def** wrapper():

print("#tomatoes#")

func()

print("~salad~")

**return** wrapper

@ingredients

@bread

**def** strange_sandwich(food="--ham--"):

print(food)

strange_sandwich()

# outputs:

##tomatoes#

# </''''''>

# --ham--

# <______/>

# ~salad~

Parameter an die Dekoratorfunktion übergeben

# 这不是什么黑色魔法(black magic),你只是必须让wrapper传递参数:

**def** a_decorator_passing_arguments(function_to_decorate):

**def** a_wrapper_accepting_arguments(arg1, arg2):

print("I got args! Look:", arg1, arg2)

function_to_decorate(arg1, arg2)

**return** a_wrapper_accepting_arguments

# 当你调用装饰器返回的函数式,你就在调用wrapper,而给wrapper的

# 参数传递将会让它把参数传递给要装饰的函数

@a_decorator_passing_arguments

**def** print_full_name(first_name, last_name):

print("My name is", first_name, last_name)

print_full_name("Peter", "Venkman")

# outputs:

# I got args! Look: Peter Venkman

# My name is Peter Venkman

Dekorator mit Parametern

Im obigen Dekoratoraufruf, z. B. @decorator, verwendet der Dekorator standardmäßig die folgende Funktion ist der einzige Parameter. Die Syntax von Dekoratoren ermöglicht es uns, beim Aufruf von Dekoratoren andere Parameter bereitzustellen, z. B. @decorator(a). Dies bietet eine größere Flexibilität beim Schreiben und Verwenden von Dekoratoren.

# a new wrapper layer

**def** pre_str(pre=''):

# old decorator

**def** decorator(F):

**def** new_F(a, b):

print(pre + " input", a, b)

**return** F(a, b)

**return** new_F

**return** decorator

# get square sum

@pre_str('^_^')

**def** square_sum(a, b):

**return** a ** 2 + b ** 2

# get square diff

@pre_str('T_T')

**def** square_diff(a, b):

**return** a ** 2 - b ** 2

print(square_sum(3, 4))

print(square_diff(3, 4))

# outputs:

# ('^_^ input', 3, 4)

# 25

# ('T_T input', 3, 4)

# -7

Der obige pre_str ist ein Dekorator, der Parameter zulässt. Es handelt sich tatsächlich um eine Funktionskapselung des ursprünglichen Dekorators und gibt einen Dekorator zurück. Wir können es als einen Verschluss verstehen, der Umweltparameter enthält. Wenn wir @pre_str(‘^_^’) aufrufen, kann Python diese Kapselungsebene erkennen und die Parameter an die Decorator-Umgebung übergeben. Dieser Aufruf entspricht:

square_sum = pre_str('^_^') (square_sum)

Dekoration „Methode in der Klasse“

Eines der großartigen Dinge an Python ist, dass Methoden und Funktionen wirklich gleich sind), außer dass die Der erste Parameter der Methode sollte ein Verweis auf das aktuelle Objekt (also self) sein. Das heißt, solange Sie daran denken, sich selbst zu berücksichtigen, können Sie Dekoratoren für Methoden auf die gleiche Weise erstellen:

def method_friendly_decorator(method_to_decorate):

**def** wrapper(self, lie):

lie = lie - 3# very friendly, decrease age even more :-)

**return** method_to_decorate(self, lie)

**return** wrapper

**class** Lucy(object):

**def** __init__(self):

self.age = 32

@method_friendly_decorator

**def** say_your_age(self, lie):

print("I am %s, what did you think?" % (self.age + lie))

l = Lucy()

l.say_your_age(-3)

# outputs: I am 26, what did you think?

Wenn Sie natürlich einen sehr allgemeinen Dekorator schreiben möchten, können Sie dies verwenden Um jede Funktion und Methode zu dekorieren, können Sie die spezifischen Parameter ignorieren und einfach *args, **kwargs direkt verwenden:

def a_decorator_passing_arbitrary_arguments(function_to_decorate):

# The wrapper accepts any arguments

**def** a_wrapper_accepting_arbitrary_arguments(*args, **kwargs):

print("Do I have args?:")

print(args)

print(kwargs)

# Then you unpack the arguments, here *args, **kwargs

# If you are not familiar with unpacking, check:

# http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/

function_to_decorate(*args, **kwargs)

**return** a_wrapper_accepting_arbitrary_arguments

@a_decorator_passing_arbitrary_arguments

**def** function_with_no_argument():

print("Python is cool, no argument here.")

function_with_no_argument()

# outputs

# Do I have args?:

# ()

# {}

# Python is cool, no argument here.

@a_decorator_passing_arbitrary_arguments

**def** function_with_arguments(a, b, c):

print(a, b, c)

function_with_arguments(1, 2, 3)

# outputs

# Do I have args?:

# (1, 2, 3)

# {}

# 1 2 3

@a_decorator_passing_arbitrary_arguments

**def** function_with_named_arguments(a, b, c, platypus="Why not ?"):

print("Do %s, %s and %s like platypus? %s" % (a, b, c, platypus))

function_with_named_arguments("Bill", "Linus", "Steve", platypus="Indeed!")

# outputs

# Do I have args ? :

# ('Bill', 'Linus', 'Steve')

# {'platypus': 'Indeed!'}

# Do Bill, Linus and Steve like platypus? Indeed!

**class** Mary(object):

**def** __init__(self):

self.age = 31

@a_decorator_passing_arbitrary_arguments

**def** say_your_age(self, lie=-3):# You can now add a default value

print("I am %s, what did you think ?" % (self.age + lie))

m = Mary()

m.say_your_age()

# outputs

# Do I have args?:

# (<__main__.Mary object at 0xb7d303ac>,)

# {}

# I am 28, what did you think?

装饰类

在上面的例子中,装饰器接收一个函数,并返回一个函数,从而起到加工函数的效果。在Python 2.6以后,装饰器被拓展到类。一个装饰器可以接收一个类,并返回一个类,从而起到加工类的效果。

def decorator(aClass):

**class** newClass:

**def** __init__(self, age):

self.total_display = 0

self.wrapped = aClass(age)

**def** display(self):

self.total_display += 1

print("total display", self.total_display)

self.wrapped.display()

**return** newClass

@decorator

**class** Bird:

**def** __init__(self, age):

self.age = age

**def** display(self):

print("My age is", self.age)

eagleLord = Bird(5)

**for** i **in** range(3):

eagleLord.display()

在decorator中,我们返回了一个新类newClass。在新类中,我们记录了原来类生成的对象(self.wrapped),并附加了新的属性total_display,用于记录调用display的次数。我们也同时更改了display方法。通过修改,我们的Bird类可以显示调用display的次数了。

内置装饰器

Python中有三种我们经常会用到的装饰器, property、 staticmethod、 classmethod,他们有个共同点,都是作用于类方法之上。

property 装饰器

property 装饰器用于类中的函数,使得我们可以像访问属性一样来获取一个函数的返回值。

class XiaoMing:

first_name = '明'

last_name = '小'

@property

**def** full_name(self):

**return** self.last_name + self.first_name

xiaoming = XiaoMing()

print(xiaoming.full_name)

例子中我们像获取属性一样获取 full_name 方法的返回值,这就是用 property 装饰器的意义,既能像属性一样获取值,又可以在获取值的时候做一些操作。

staticmethod 装饰器

staticmethod 装饰器同样是用于类中的方法,这表示这个方法将会是一个静态方法,意味着该方法可以直接被调用无需实例化,但同样意味着它没有 self 参数,也无法访问实例化后的对象。

class XiaoMing:

@staticmethod

**def** say_hello():

print('同学你好')

XiaoMing.say_hello()

# 实例化调用也是同样的效果

# 有点多此一举

xiaoming = XiaoMing()

xiaoming.say_hello()

classmethod 装饰器

classmethod 依旧是用于类中的方法,这表示这个方法将会是一个类方法,意味着该方法可以直接被调用无需实例化,但同样意味着它没有 self 参数,也无法访问实例化后的对象。相对于 staticmethod 的区别在于它会接收一个指向类本身的 cls 参数。

class XiaoMing:

name = '小明'

@classmethod

**def** say_hello(cls):

print('同学你好, 我是' + cls.name)

print(cls)

XiaoMing.say_hello()

wraps 装饰器

一个函数不止有他的执行语句,还有着 name(函数名),doc (说明文档)等属性,我们之前的例子会导致这些属性改变。

def decorator(func):

**def** wrapper(*args, **kwargs):

"""doc of wrapper"""

print('123')

**return** func(*args, **kwargs)

**return** wrapper

@decorator

**def** say_hello():

"""doc of say hello"""

print('同学你好')

print(say_hello.__name__)

print(say_hello.__doc__)

由于装饰器返回了 wrapper 函数替换掉了之前的 say_hello 函数,导致函数名,帮助文档变成了 wrapper 函数的了。解决这一问题的办法是通过 functools 模块下的 wraps 装饰器。

from functools import wraps

**def** decorator(func):

@wraps(func)

**def** wrapper(*args, **kwargs):

"""doc of wrapper"""

print('123')

**return** func(*args, **kwargs)

**return** wrapper

@decorator

**def** say_hello():

"""doc of say hello"""

print('同学你好')

print(say_hello.__name__)

print(say_hello.__doc__)

装饰器总结

装饰器的核心作用是name binding。这种语法是Python多编程范式的又一个体现。大部分Python用户都不怎么需要定义装饰器,但有可能会使用装饰器。鉴于装饰器在Python项目中的广泛使用,了解这一语法是非常有益的。

常见错误:“装饰器”=“装饰器模式”

设计模式是一个在计算机世界里鼎鼎大名的词。假如你是一名 Java 程序员,而你一点设计模式都不懂,那么我打赌你找工作的面试过程一定会度过的相当艰难。

但写 Python 时,我们极少谈起“设计模式”。虽然 Python 也是一门支持面向对象的编程语言,但它的鸭子类型设计以及出色的动态特性决定了,大部分设计模式对我们来说并不是必需品。所以,很多 Python 程序员在工作很长一段时间后,可能并没有真正应用过几种设计模式。

不过装饰器模式(Decorator Pattern)是个例外。因为 Python 的“装饰器”和“装饰器模式”有着一模一样的名字,我不止一次听到有人把它们俩当成一回事,认为使用“装饰器”就是在实践“装饰器模式”。但事实上,它们是两个完全不同的东西。

“装饰器模式”是一个完全基于“面向对象”衍生出的编程手法。它拥有几个关键组成:一个统一的接口定义、若干个遵循该接口的类、类与类之间一层一层的包装。最终由它们共同形成一种“装饰”的效果。

而 Python 里的“装饰器”和“面向对象”没有任何直接联系,**它完全可以只是发生在函数和函数间的把戏。事实上,“装饰器”并没有提供某种无法替代的功能,它仅仅就是一颗“语法糖”而已。下面这段使用了装饰器的代码:

@log_time

@cache_result

**def** foo(): pass

基本完全等同于:

def foo(): pass

foo = log_time(cache_result(foo))

装饰器最大的功劳,在于让我们在某些特定场景时,可以写出更符合直觉、易于阅读的代码。它只是一颗“糖”,并不是某个面向对象领域的复杂编程模式。

Das obige ist der detaillierte Inhalt vonPython-Dekoratoren verstehen und anwenden. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:51cto.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen