Heim  >  Artikel  >  Backend-Entwicklung  >  Zusammenfassung der Verwendung von Dekoratoren in Python

Zusammenfassung der Verwendung von Dekoratoren in Python

零下一度
零下一度Original
2017-06-29 15:34:321190Durchsuche

Ich lerne kürzlich Python. Das Folgende ist, was in der Python-Lerngruppe vorgestellt wurde. Jetzt zu lernen und mehr zu üben, ist eine gute Möglichkeit, es zu lernen.

Python hat viele leistungsstarke Möglichkeiten und durchdachte Funktionen. Wenn Sie die beliebtesten Rankings auflisten möchten, sind Dekorateure auf jeden Fall dabei.

Wenn Sie den Dekorateur zum ersten Mal treffen, werden Sie sich elegant und magisch fühlen. Aber wenn Sie es mit Ihren eigenen Händen verwirklichen möchten, spüren Sie immer ein Gefühl der Distanz, genau wie die Eisschönheit im Boudoir. Dies liegt häufig daran, dass beim Verständnis von Dekorateuren andere Konzepte miteinander vermischt werden. Wenn ich die Schleierschichten entferne, werden Sie sehen, dass reine Dekoration eigentlich ganz einfach und unkompliziert ist.

Das Prinzip des Dekorators

Führen Sie ein Beispiel eines Dekorators unter dem Interpreter aus, um es intuitiv zu spüren.
# make_bold ist der Dekorator, die Implementierungsmethode wird hier weggelassen


>>> @make_bold
... def get_content():
...  return 'hello world'
...
>>> get_content()
&#39;<b>hello world</b>&#39;

get_content dekoriert durch make_bold, das nach dem Aufruf zurückgegebene Ergebnis wird automatisch zurückgegeben getaggt mit b Cover. Wie geht das? Sie können es in 4 einfachen Schritten verstehen.

1. Die Funktion ist ein Objekt

Wir definieren eine get_content-Funktion. Zu diesem Zeitpunkt ist get_content auch ein Objekt und kann Operationen für alle Objekte ausführen.


def get_content():
  return &#39;hello world&#39;

Es hat id , hat type und hat value.


>>> id(get_content)
140090200473112
>>> type(get_content)
<class &#39;function&#39;>
>>> get_content
<function get_content at 0x7f694aa2be18>

kann wie andere Objekte anderen Variablen zugewiesen werden.


>>> func_name = get_content
>>> func_name()
&#39;hello world&#39;

Es kann als Parameter oder als Rückgabewert übergeben werden


>>> def foo(bar):
...   print(bar())
...   return bar
...
>>> func = foo(get_content)
hello world
>>> func()
&#39;hello world&#39;

2. Benutzerdefinierte FunktionObjekt

Wir können eine Klasse verwenden, um FunktionObjekt zu erstellen. Das Funktionsobjekt mit dem Mitgliedsfunktionsaufruf ist das Funktionsobjekt, das beim Aufruf des Funktionsobjekts aufgerufen wird.


class FuncObj(object):
  def init(self, name):
    print(&#39;Initialize&#39;)
    self.name= name

  def call(self):
    print(&#39;Hi&#39;, self.name)

Nennen wir es. Wie Sie sehen, ist die Verwendung von Funktionsobjekten in zwei Schritte unterteilt: Konstruktion und Aufruf (Studenten, bitte achten Sie darauf, dies ist der Testpunkt).


>>> fo = FuncObj(&#39;python&#39;)
Initialize
>>> fo()
Hi python

3. @ ist syntaktischer Zucker

Das @ des Dekorators macht nichts Besonderes, Sie können das Gleiche erreichen Ohne diese Funktionalität ist lediglich mehr Code erforderlich.


@make_bold
def get_content():
  return &#39;hello world&#39;

# 上面的代码等价于下面的

def get_content():
  return &#39;hello world&#39;
get_content = make_bold(get_content)

make_bold ist eine Funktion, bei der der Eingabeparameter ein Funktionsobjekt und der Rückgabewert ein Funktionsobjekt sein muss. Der syntaktische Zucker von @ eliminiert tatsächlich die letzte Codezeile oben und macht sie dadurch besser lesbar. Nach der Verwendung des Dekorators wird bei jedem Aufruf von get_content das von make_bold zurückgegebene Funktionsobjekt tatsächlich aufgerufen.

4. Verwenden Sie Klassen, um Dekoratoren zu implementieren.

Der Eingabeparameter ist ein Funktionsobjekt und der Rückgabeparameter ist ein Funktionsobjekt, wenn der Konstruktor der Klasse in Schritt 2 geändert wird Wenn der Parameter doch ein Funktionsobjekt ist, erfüllt er genau die Anforderungen? Versuchen wir, make_bold zu implementieren.


class make_bold(object):
  def init(self, func):
    print(&#39;Initialize&#39;)
    self.func = func

  def call(self):
    print(&#39;Call&#39;)
    return &#39;<b>{}</b>&#39;.format(self.func())

Es ist geschafft, mal sehen, ob es funktioniert.


>>> @make_bold
... def get_content():
...   return &#39;hello world&#39;
...
Initialize
>>> get_content()
Call
&#39;<b>hello world</b>&#39;

Dekorateur erfolgreich umgesetzt! Ist es nicht ganz einfach?

Hier ist eine Analyse der beiden Prozesse des Aufbaus und der Berufung, die zuvor hervorgehoben wurden. Entfernen wir den @-Syntaxzucker, um das Verständnis zu erleichtern.
# Konstruktion: Wenn Sie den Dekorator zum Erstellen des Funktionsobjekts verwenden, rufen Sie init auf


>>> get_content = make_bold(get_content)
Initialize

# 调用,实际上直接调用的是make_bold构造出来的函数对象
>>> get_content()
Call
&#39;<b>hello world</b>&#39;

An diesem Punkt ist es völlig klar und Sie können fertig werden Blumen ausbreiten. Schließen Sie die Webseite ~~~ (Wenn Sie nur das Prinzip von Dekoratoren kennenlernen möchten)

Funktionsversion des Dekorators

Wenn Sie den Quellcode lesen, Sie sehen häufig die Verwendung verschachtelter Funktionen. Wie ist der implementierte Dekorator zu verstehen? Es sind nur 4 Schritte erforderlich.

1. Funktionsobjektinitialisierung von def

Es ist leicht zu erkennen, wann das mit der Klasse implementierte Funktionsobjekt erstellt wird, aber wann wird das durch def definierte Funktionsobjekt erstellt?
# Die globalen Variablen hier haben irrelevanten Inhalt gelöscht


>>> globals()
{}
>>> def func():
...   pass
...
>>> globals()
{&#39;func&#39;: <function func at 0x10f5baf28>}

Im Gegensatz zu einigen kompilierten Sprachen wurde die Funktion erstellt, wenn das Programm ordnungsgemäß startet. Wie Sie dem obigen Beispiel entnehmen können, wird ein Funktionsobjekt erstellt, bis def ausgeführt und der Variablen make_bold zugewiesen wird.

Die Wirkung dieses Codes ist dem folgenden Code sehr ähnlich.


class NoName(object):
  def call(self):
    pass

func = NoName()

2. Verschachtelte Funktionen

Python-Funktionen können verschachtelte Definitionen sein.


def outer():
  print(&#39;Before def:&#39;, locals())
  def inner():
    pass
  print(&#39;After def:&#39;, locals())
  return inner

Inner ist innerhalb von Outer definiert und wird daher als lokale Variable von Outer gezählt. Das Funktionsobjekt wird erst erstellt, wenn def inner ausgeführt wird. Daher wird bei jedem Aufruf von „outer“ ein neues „Inner“ erstellt. Wie unten zu sehen ist, ist das zurückgegebene Innere jedes Mal unterschiedlich.


>>> outer()
Before def: {}
After def: {&#39;inner&#39;: <function outer.<locals>.inner at 0x7f0b18fa0048>}
<function outer.<locals>.inner at 0x7f0b18fa0048>
>>> outer()
Before def: {}
After def: {&#39;inner&#39;: <function outer.<locals>.inner at 0x7f0b18fa00d0>}
<function outer.<locals>.inner at 0x7f0b18fa00d0>

3. Abschlüsse

Was ist das Besondere an verschachtelten Funktionen? Weil es Schließung gibt.


def outer():
  msg = &#39;hello world&#39;
  def inner():
    print(msg)
  return inner

Der folgende Test zeigt, dass Inner auf die lokale Variable msg von Outer zugreifen kann.


>>> func = outer()
>>> func()
hello world

Verschlüsse haben zwei Eigenschaften
1. Inner kann auf Variablen (lokale Variablen) im Namensraum von Outer und seinen Vorgängerfunktionen zugreifen Funktionsparameter).
2. Der Aufruf von Outer ist zurückgekehrt, aber sein Namespace wird vom zurückgegebenen Inner-Objekt referenziert, sodass er noch nicht recycelt wird.

Wenn Sie tiefer in diesen Teil einsteigen möchten, können Sie mehr über die LEGB-Regeln von Python erfahren.

4. Verwenden Sie Funktionen zum Implementieren von Dekoratoren.

Der Dekorator erfordert, dass der Eingabeparameter ein Funktionsobjekt und der Rückgabewert ein Funktionsobjekt ist. Verschachtelte Funktionen sind vollständig funktionsfähig.


def make_bold(func):
  print(&#39;Initialize&#39;)
  def wrapper():
    print(&#39;Call&#39;)
    return &#39;<b>{}</b>&#39;.format(func())
  return wrapper

用法跟类实现的装饰器一样。可以去掉 @ 语法糖分析下 构造 和 调用 的时机。


>>> @make_bold
... def get_content():
...   return &#39;hello world&#39;
...
Initialize
>>> get_content()
Call
&#39;<b>hello world</b>&#39;

因为返回的 wrapper 还在引用着,所以存在于 make_bold 命名空间的 func 不会消失。 make_bold 可以装饰多个函数, wrapper 不会调用混淆,因为每次调用 make_bold ,都会有创建新的命名空间和新的 wrapper 。

到此函数实现装饰器也理清楚了,完结撒花,可以关掉网页了~~~(后面是使用装饰的常见问题)

常见问题

1. 怎么实现带参数的装饰器?

带参数的装饰器,有时会异常的好用。我们看个例子。


>>> @make_header(2)
... def get_content():
...   return &#39;hello world&#39;
...
>>> get_content()
&#39;<h2>hello world</h2>&#39;

怎么做到的呢?其实这跟装饰器语法没什么关系。去掉 @ 语法糖会变得很容易理解。


@make_header(2)
def get_content():
  return &#39;hello world&#39;

# 等价于

def get_content():
  return &#39;hello world&#39;
unnamed_decorator = make_header(2)
get_content = unnamed_decorator(get_content)

上面代码中的 unnamed_decorator 才是真正的装饰器, make_header 是个普通的函数,它的返回值是装饰器。

来看一下实现的代码。


def make_header(level):
  print(&#39;Create decorator&#39;)

  # 这部分跟通常的装饰器一样,只是wrapper通过闭包访问了变量level
  def decorator(func):
    print(&#39;Initialize&#39;)
    def wrapper():
      print(&#39;Call&#39;)
      return &#39;<h{0}>{1}</h{0}>&#39;.format(level, func())
    return wrapper

  # make_header返回装饰器
  return decorator

看了实现代码,装饰器的 构造 和 调用 的时序已经很清楚了。


>>> @make_header(2)
... def get_content():
...   return &#39;hello world&#39;
...
Create decorator
Initialize
>>> get_content()
Call
&#39;<h2>hello world</h2>&#39;

2. 如何装饰有参数的函数

为了有条理地理解装饰器,之前例子里的被装饰函数有意设计成无参的。我们来看个例子。


@make_bold
def get_login_tip(name):
  return &#39;Welcome back, {}&#39;.format(name)

最直接的想法是把 get_login_tip 的参数透传下去。


class make_bold(object):
  def init(self, func):
    self.func = func

  def call(self, name):
    return &#39;<b>{}</b>&#39;.format(self.func(name))

如果被装饰的函数参数是明确固定的,这么写是没有问题的。但是 make_bold 明显不是这种场景。它既需要装饰没有参数的 get_content ,又需要装饰有参数的 get_login_tip 。这时候就需要可变参数了。


class make_bold(object):
  def init(self, func):
    self.func = func
  def call(self, *args, **kwargs):
    return &#39;<b>{}</b>&#39;.format(self.func(*args, **kwargs))

当装饰器不关心被装饰函数的参数,或是被装饰函数的参数多种多样的时候,可变参数非常合适。可变参数不属于装饰器的语法内容,这里就不深入探讨了。

3. 一个函数能否被多个装饰器装饰?

下面这么写合法吗?


@make_italic
@make_bold
def get_content():
  return &#39;hello world&#39;

合法。上面的的代码和下面等价,留意一下装饰的顺序。


def get_content():
  return &#39;hello world&#39;
get_content = make_bold(get_content) # 先装饰离函数定义近的
get_content = make_italic(get_content)

4. functools.wraps 有什么用?

Python的装饰器倍感贴心的地方是对调用方透明。调用方完全不知道也不需要知道调用的函数被装饰了。这样我们就能在调用方的代码完全不改动的前提下,给函数patch功能。

为了对调用方透明,装饰器返回的对象要伪装成被装饰的函数。伪装得越像,对调用方来说差异越小。有时光伪装函数名和参数是不够的,因为Python的函数对象有一些元信息调用方可能读取了。为了连这些元信息也伪装上, functools.wraps 出场了。它能用于把被调用函数的 module , name , qualname , doc , annotations 赋值给装饰器返回的函数对象。


import functools

def make_bold(func):
  @functools.wraps(func)
  def wrapper(*args, **kwargs):
    return &#39;<b>{}</b>&#39;.format(func(*args, **kwargs))
  return wrapper

对比一下效果。


>>> @make_bold
... def get_content():
...   &#39;&#39;&#39;Return page content&#39;&#39;&#39;
...   return &#39;hello world&#39;

# 不用functools.wraps的结果
>>> get_content.name
&#39;wrapper&#39;
>>> get_content.doc
>>>

# 用functools.wraps的结果
>>> get_content.name
&#39;get_content&#39;
>>> get_content.doc
&#39;Return page content&#39;

实现装饰器时往往不知道调用方会怎么用,所以养成好习惯加上 functools.wraps 吧。

这次是真·完结了,撒花吧~~~

Das obige ist der detaillierte Inhalt vonZusammenfassung der Verwendung von Dekoratoren in Python. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn