Heim  >  Artikel  >  Backend-Entwicklung  >  Eingehende Untersuchung von Python decorators_python

Eingehende Untersuchung von Python decorators_python

不言
不言Original
2018-04-08 11:11:361225Durchsuche

Dieser Artikel bietet hauptsächlich eine detaillierte Untersuchung der Informationen zum Python-Dekorator. Was ist ein Dekorateur? Die von Dekorateuren befolgten Prinzipien haben einen gewissen Referenzwert. Interessierte Freunde können sich in unserem Softwareprodukt-Upgrade auf

Was ist ein Dekorateur?> beziehen. Manchmal müssen wir neue hinzufügen In unseren Softwareprodukten kann es vorkommen, dass dieselbe Funktion hunderte Male aufgerufen wird. Wenn wir sie einzeln ändern, werden unsere Programmierer das tun Dumm, ändere einfach die Funktionsdefinition! Klassenkamerad, bitte wach auf, was ist, wenn die neu hinzugefügte Funktion die Parameter oder den Rückgabewert ändert?). Jetzt ist es an der Zeit, dass unser Dekorateur seine Magie zeigt. Dekorateure können die Funktion erreichen, der Funktion neue Funktionen hinzuzufügen, ohne die Aufrufform der ursprünglichen Funktion zu ändern (dh die transparente Verarbeitung der Funktion). Die Umsetzung sowie das Umsetzungsprinzip werden im Folgenden ausführlich erläutert.

Prinzipien, die von Dekorateuren befolgt werden

Dekorateure spielen, wie der Name schon sagt, die Rolle der Dekoration. Da es sich um Dekoration handelt, ist das dekorierte Objekt das, was es nicht sein kann Es gibt nicht die geringste Änderung. Hier müssen wir beim Schreiben von Dekoratoren das eiserne Gesetz begreifen, dass der Quellcode der geänderten Funktion nicht geändert werden kann.

Um diesem eisernen Gesetz zu folgen, müssen wir noch einige Vorbereitungen treffen. Zuerst müssen wir drei Konzepte verstehen:

Der Funktionsname ist „variabel“

In Python ist der Funktionsname tatsächlich wie ein Funktionszeiger in der C-Sprache, der unsere Funktionsadresse darstellt. Erst wenn der Interpreter diese Adresse erhält, führt er diesen Speichercode aus. Daher unterscheidet sich der Funktionsname im Wesentlichen nicht von verschiedenen Variablen, außer dass der Funktionsname und der Speicher, auf den die gewöhnliche Variable verweist, auf unterschiedliche Weise verwendet werden. Diese werden durch den Mechanismus des zugrunde liegenden Interpreters bestimmt. sind beide transparent, sodass wir annehmen können, dass es keinen Unterschied zwischen den beiden gibt.

Funktionen höherer Ordnung

Was eine Funktion höherer Ordnung ist, ist eigentlich sehr einfach. Begreifen Sie einfach zwei Prinzipien:

Formale Parameter haben Funktionsnamen
  • Rückgabewerte haben Funktionsnamen

  • Solange einer Ist eines dieser beiden Prinzipien erfüllt, kann es als Funktion höherer Ordnung bezeichnet werden. Rückblickend erscheint hier der Funktionsname, den wir oben erwähnt haben. Wenn Sie darüber nachdenken, behandeln wir ihn hier nicht als tatsächlichen Parameter?

Verschachtelte Funktion

ZahlWas eine verschachtelte Funktion ist, ist eigentlich sehr einfach, Just ein Prinzip erfassen:

Definieren Sie eine andere Funktion im Funktionskörper einer Funktion

  • Was hier hervorgehoben werden muss Ja, wann Wenn eine Funktion definiert ist, wird der Funktionskörper nicht ausgeführt. Genauso wie bei der Definition einer Variablen der Inhalt der Variablen nicht gelesen wird. Dies ist entscheidend und sehr hilfreich für uns, um das Prinzip der Decorator-Implementierung zu verstehen.

Wie man einen Dekorateur schreibt

Mit der obigen Vorahnung erklären wir jetzt im Detail, wie man einen Dekorator schreibt, damit es viel einfacher zu verstehen ist.

Das Wesen des Dekorators

Tatsächlich ist ein Dekorator im Wesentlichen eine Funktion, die auch einen Funktionsnamen, Parameter und einen Rückgabewert hat. Aber in Python verwenden wir „@auth“, um es darzustellen.

@auth    # 其等价于:func = auth(func)
def func():
  print("func called")
In diesem Beispiel wird gezeigt, wie das Format der Funktion func in Python geändert wird. Natürlich haben wir unsere Dekoratorfunktion noch nicht implementiert. Worauf wir achten müssen, ist der in den Kommentaren geschriebene Inhalt. Wir können Folgendes sehen:

Die Dekoratorfunktion ist tatsächlich eine Funktion höherer Ordnung (sowohl Parameter als auch Rückgabewerte). sind Funktionsnamen).
  • „auth(func)“ ruft unsere Dekoratorfunktion auf, das heißt, der Funktionskörper der Dekoratorfunktion wird unbedingt ausgeführt.

Designidee

Da der Dekorator eine Funktion ist, weist er die oben eingeführte Äquivalenzbeziehung auf. Dann können wir unseren Dekorator wie folgt entwerfen:

Definieren Sie eine neue Funktion im Funktionskörper unseres Dekorators und rufen Sie die geänderte Funktion in dieser neu definierten Funktion auf. Währenddessen fügen Funktionen neue Funktionen hinzu im Kontext der geänderten Funktion. Verwenden Sie abschließend den Rückgabewert der Dekoratorfunktion, um den Funktionsnamen unserer neu definierten Funktion zurückzugeben.

  • Daraus können wir erkennen, dass der Rückgabewert func in „func = auth(func)“ den Funktionsnamen der neu definierten Funktion im Decorator darstellt.
  • Ich habe vorher viel vorbereitet, nur um den Implementierungsmechanismus des Dekorators zu enthüllen. Tatsächlich ist es nichts, es ist sehr einfach:
    • 装饰器机制改变了被修饰函数的函数名表示的地址数据。说白了就是,被修饰前,函数名代表的是A内存块;被修饰后,函数名代表的是B内存块;只不过,在执行B内存块时,会调用A内存块罢了。B内存块中的代码就是我们新加的功能。而这种机制的实现,使用了“高阶函数”和“嵌套函数”的机制。

    • 最终的效果就是,但在调用被修饰过的函数时,其实调用的不是原来的内存块,而是修饰器新申请的内存块。

    第一步:设计装饰器函数

    装饰器函数定义跟普通函数定义没什么区别,关键是函数体怎么写的问题。这里,为了便于理解,先用无参数的装饰器函数说明。

    #装饰器函数定义格式
    def deco(func):
      '''函数体...'''
    return func

    这里说的无参数,指的是没有除了“func”之外的参数
    难点是函数体的编写,下面的示例先告诉你为什么要有第二步:

    #使用语法糖@来装饰函数,相当于“myfunc = deco(myfunc)”
    def deco(func):
      print("before myfunc() called.")
      func()
      print("after myfunc() called.")
      return func
     
    @deco
    def myfunc():
      print("myfunc() called.")
     
    myfunc()
    myfunc()
     
    #output:
    before myfunc() called.
    myfunc() called.
    after myfunc() called.
    myfunc() called.
    myfunc() called.

    由输出结果可以看出,我们的装饰器并没有生效。别跟我说装饰器只生效了一次,那是大家忽略了“@deco”的等效机制。解释到“@deco”时,会解释成“myfunc = deco(myfunc)”。注意了,前面我提到了,这里其实在调用deco函数的,因此,deco的函数体会被执行。所以output的前三行并不是调用myfunc函数时产生的效果,那有怎能说装饰器生效了一次呢?第二步就是解决装饰器没生效的问题的。

    第二步:包装被修饰函数

    #基本格式
    def deco(func):
      def _deco()
        #新增功能
        #...
        #...
        func() #别修饰函数调用
      return_deco

     下面给出个示例:

    #使用内嵌包装函数来确保每次新函数都被调用,
    #内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象
     
    def deco(func):
      def _deco():
        print("before myfunc() called.")
        func()
        print("after myfunc() called.")
        # 不需要返回func,实际上应返回原函数的返回值
      return _deco
     
    @deco
    def myfunc():
      print("myfunc() called.")
      return 'ok'
     
    myfunc()
     
    #output:
    before myfunc() called.
    myfunc() called.
    after myfunc() called.

      第三步:被修饰函数参数和返回值透明化处理

    当完成了第二步时,其实装饰器已经完成了主要部分,下面就是对被修饰函数的参数和返回值的处理。这样才能真正实现装饰器的铁律。话不多说,直接上代码:

    #基本格式
    def deco(func):
      def _deco(*args, **kwargs) #参数透明化
        #新增功能
        #...
        #...
        res = func(*args, **kwargs) #别修饰函数调用
        return res #返回值透明化
      return_deco

    通过上面的分析知:

    参数透明化:当我们在调用被装饰后的函数时,其实调用的时这里的_deco函数。那么,我们就给_deco函数加上可变参数,并把得到的可变参数传递给func函数不就可以了。
    返回值透明化:和参数透明化同理,给_deco函数定义返回值,并返回func的返回值就可以了。

    透明化处理就是这么简单!至此,我们的装饰器编写完成。给个示例吧:

    #对带参数的函数进行装饰,
    #内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象
     
    def deco(func):
      def _deco(*agrs, **kwagrs):
        print("before myfunc() called.")
        ret = func(*agrs, **kwagrs)
        print(" after myfunc() called. result: %s" % ret)
        return ret
      return _deco
     
    @deco
    def myfunc(a, b):
      print(" myfunc(%s,%s) called." % (a, b))
      return a + b
     
    print("sum=",myfunc(1, 2))
    print("sum=",myfunc(3, 4))
     
    #output:
    before myfunc() called.
     myfunc(1,2) called.
     after myfunc() called. result: 3
    sum= 3
    before myfunc() called.
     myfunc(3,4) called.
     after myfunc() called. result: 7
    sum= 7

    装饰器进阶

    带参数装饰器

    装饰器即然也是函数,那么我们也可以给其传递参数。我这里说的是:“@auth(auth_type = 'type1')”这中形式哟。先上个代码吧:

    #基本格式
    def deco(deco_type)
      def _deco(func):
        def __deco(*args, **kwargs) #参数透明化
          #新增功能
          #...
          #...
          print("deco_type:",deco_type) #使用装饰器参数
          res = func(*args, **kwargs) #别修饰函数调用
          return res #返回值透明化
        return __deco
      return_deco

     说白了,就是在原来的装饰器的基础上再在最外层套一个deco函数,并用其来接收装饰器参数。由于是在最外层套了一个函数,那么这个函数的形参的作用范围就是函数体内部,所以里面的函数定义中随便用啦,就这么任性。
    那怎么理解解释器的解析过程呢?在这里,只要我们明白一点就好,那就是: “@auth(auth_type = 'type1')”等价于“func = auth(auth_type = 'type1')(func)” 解释器会先翻译“auth(auth_type = 'type1')”,再将其返回值(假设给了_func这个不存在的函数名)当作函数指针,这里的_func函数名代表的是_deco,然后再去执行“func = _func(func)”,而这个func函数名代表的其实就是__deco。

    至此,就达到了通过装饰器来传参的目的。给个示例吧:

    #示例7: 在示例4的基础上,让装饰器带参数,
    #和上一示例相比在外层多了一层包装。
    #装饰函数名实际上应更有意义些
     
    def deco(deco_type):
      def _deco(func):
        def __deco(*args, **kwagrs):
          print("before %s called [%s]." % (func.__name__, deco_type))
          func(*args, **kwagrs)
          print(" after %s called [%s]." % (func.__name__, deco_type))
        return __deco
      return _deco
     
    @deco("mymodule")
    def myfunc():
      print(" myfunc() called.")
     
    @deco("module2")
    def myfunc2():
      print(" myfunc2() called.")
     
    myfunc()
    myfunc2()
     
    #output:
    before myfunc called [mymodule].
     myfunc() called.
     after myfunc called [mymodule].
    before myfunc2 called [module2].
     myfunc2() called.
     after myfunc2 called [module2].

    多重装饰器修饰函数

    如果说,我上面说的内容都理解了,那么这个东东,就太简单不过了。不就是把我们的是装饰器当中被修饰的函数,对它进行装饰吗?但我在这里还想说的是,我们换个角度看问题。我们的关注点放在原来的被修饰的函数上,就会发现,NB呀,我可以给它添加若干个功能撒。给个示例吧:

    def deco(deco_type):
      def _deco(func):
        def __deco(*args, **kwagrs):
          print("before %s called [%s]." % (func.__name__, deco_type))
          func(*args, **kwagrs)
          print(" after %s called [%s]." % (func.__name__, deco_type))
        return __deco
      return _deco
     
    @deco("module1")
    @deco("mymodule")
    def myfunc():
      print(" myfunc() called.")
     
    @deco("module2")
    def myfunc2():
      print(" myfunc2() called.")
     
    myfunc()
     
    #output:
    before __deco called [module1].
    before myfunc called [mymodule].
     myfunc() called.
     after myfunc called [mymodule].
     after __deco called [module1].

     注意结果哟,@deco("module1"),来修饰的deco("mymdule")的,和我们想的是一样的,完美!

    相关推荐:

    深度理解Python装饰器的概念和含义

    Eine einfache Erklärung von Python-Dekoratoren

Das obige ist der detaillierte Inhalt vonEingehende Untersuchung von Python decorators_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