zoukankan      html  css  js  c++  java
  • Python装饰器的原理与应用

    Python装饰器

    装饰器是什么东西呢?就是起到装饰作用的这么一个函数,那这玩意有啥用呢?很多人都会抛出这样的疑问,咱们就通俗的说一下这个装饰器到底是什么东西,是什么工作原理呢?

    什么是装饰器?

    首先,装饰顾名思义就是装饰用,为什么需要对函数装饰呢?是因为要遵循一个“开放”--“封闭”原则,因为已经写好的函数可能已经经过很久的测试和应用,没有出现任何问题的函数体,如果对函数内部进行修改势必会造成一些意外的问题,另外,这个函数很有可能不是你自己写的,那么对函数内部结构不熟悉,变量的不熟悉等等也可能导致函数的运行出现问题。那么装饰器就起到了不改变原函数的功能和不改动原函数内部代码的前提下,给函数添加功能的作用。

    就如同一个面包,想要增加奶油或果糖,如果抛开面包加入到面包中,不单单影响美观,还破坏了面包本身。那么在面包外层涂上奶油或果糖等等,不仅能达到想要的效果,还不会破坏面包本身。。这就是装饰器。

    装饰器的基本形式

    一般情况下,我们可以在要增加功能的函数前加上另外一个函数,来达到目的。

    例如:

     1 def func_new():
     2     print('这里是func_new函数的输出。')
     3     func()                     #在函数内部调用原函数
     4     print('调用完毕')
     5 
     6 def func():
     7     print('这里是原函func数的输出')
     8 
     9 func_new()
    

    #输出结果: >>>这里是func_new函数的输出。 >>>这里是原函func数的输出 >>>调用完毕

    虽然起到了装饰作用,也没有改变原函数内部,但我们在调用的时候却改变了调用的函数名。这在日常工作中是不可以的,因为原来的函数可能被很多部门或很多程序调用,那么如果为了增加函数的功能却改变其他程序或部门的调用那简直就是灾难。

    如何让函数的调用依旧是原来的调用方式不改变,然后对这个函数增加功能呢?

    让我们在执行原函数前执行其他函数,并且在函数没有被调用时不执行其他函数的调用。

    如果:func = funcnew(func) 看似可以使用func()来调用原函数,但是,funcnew(func)在赋值给func变量之前就已经被执行了。这样是不对的。那么如何让func_new(func)赋值前不执行呢? 这里就用到了嵌套函数

    def func_new(func):
        def inner():        #嵌套一个函数inner
            print('这里是func_new函数的输出。')
            func()                     #在函数内部调用原函数
            print('调用完毕')
        return inner        #返回inner的内存地址。  (不要加括号哦,加了括号就是执行了。失去这个嵌套函数的意义了.)

    这样做一个嵌套,那么当 func = funcnew(func) 时 funcnew(func) 不会被执行,而是返回了一个函数内存地址,这是因为func_new虽然被执行了。但返回的是inner,而inner没有被执行而返回的是inner的内存地址,所以,func得到的是一个函数内存地址。

    func = func_new(func)   #这时变量func得到的是内存地址,func_new()函数没有执行。
    func()          #与原函数名一致,执行后是修饰过的结果。

    这时,函数装饰器的原理就搞定了。那么说,如果每次给函数添加装饰器都要做赋值操作,这样太麻烦了。。。是的,python调用装饰器时用@。使用装饰器时,在要装饰的函数上方,加上@装饰器函数名。------看代码。

    @func_new    #调用装饰器函数
    def func():
        print('这里是原函func数的输出')
    
    func()  #执行函数,功能实现。

    是不是明白了一些呢?让我们把装饰器基本型列出来,从头再来看一下。。

    装饰器基本型: 
    def decorator():      #这里是装饰器参数,装饰器也可以带入参数,函数调用装饰器时如果是带有参数的装饰器需要@decorator(参数)
        def outer(func):   #这里的func是需要装饰的函数名,使用@调用装饰器时,函数名会被自动带入。
            def inner(*args,**kwargs): #非固定参数是被调用函数的参数。
                    ''' 这里是需要
                        装饰的内容'''
                return func(*args,**kwargs)      #在装饰内容执行完毕后,执行原函数。
            return inner
        return outer

    让我们引用一个简单的小实例来看看装饰器如何使用。

     1 # 这是一个记录程序运行时间的小程序,用装饰器来对执行进行认证,并由装饰器来记录每个函数执后所需的时间。
     2 import time
     3 
     4 pwd = '123'
     5 swith = False
     6 def login(func):    #实现认证执行的装饰器
     7     def inner(*args,**kwargs):   #为了能使函数调用到参数,不管套用了多少层的装饰器,都需要加入非固定参数
     8         global swith
     9         if not swith:
    10             n = input('>>>')
    11             if n == pwd:
    12                 swith = True
    13                 return func(*args,**kwargs)
    14             else:
    15                 return
    16         else:
    17             return func(*args,**kwargs)
    18     return inner
    19 
    20 
    21 def sum_t(func):    #记录程序执行时间的装饰器
    22     def inner(*args,**kwargs):  #接受函数的参数
    23         start = time.time()
    24         f = func(*args,**kwargs)
    25         end = time.time()
    26         print(end - start)
    27         return f   #返回函数返回值
    28     return inner     #装饰器中的功能一定要包含调用装饰器函数的所有功能。否则得不到正确返回值或出错!
    29 
    30 #三种不同形式的函数
    31 @login      #先调用 认证装饰器,认证通过后执行函数功能。
    32 @sum_t      #通过认证后,调用时间记录装饰器
    33 def f1():      #不带参数和返回值的函数
    34     time.sleep(2)
    35     print('F1')
    36 
    37 @login
    38 @sum_t
    39 def f2(a):       #带参数的函数。
    40     time.sleep(2)
    41     print('F2',a)
    42 
    43 @login
    44 @sum_t
    45 def f3():   #带return 返回值的函数
    46     time.sleep(2)
    47     print('F3')
    48     return '结束'   #函数中,如果有返回值在装饰器中也需要相应的加入对应的返回项
    49 
    50 if __name__ == '__main__':
    51     f1()
    52     f2('OK')
    53     f = f3()
    54     print(f)
    #执行结果:
    >>>123          #输入密码通过后,依次执行。  
    F1
    2.0004823207855225
    F2 'ok'
    2.0012047290802
    F3
    2.000471353530884
    '结束'
  • 相关阅读:
    [歌词]世界末日
    AJAX是什么?
    [转]Moving Your Access 2002 Database to SQL Server
    .net from身份验证的配置介绍
    ajax
    cmd常用命令
    查看端口解除端口占用
    好久没进来了,今天发一个原创的DatePicker
    真倒霉,前不久分區表錯誤把我數據全部搞沒了
    DataGrid利用DataView过滤,排序
  • 原文地址:https://www.cnblogs.com/sly27/p/8707191.html
Copyright © 2011-2022 走看看