python3.x 基礎三:裝飾器


裝飾器:本質是函數,用於裝飾其他函數,在不改變其他函數的調用和代碼的前提下,增加新功能

原則:

1.不能修改被裝飾函數的源代碼

2.不能修改被裝飾函數的調用方式

3.裝飾函數對於被裝飾函數透明

參考如下鏈接:

http://egon09.blog.51cto.com/9161406/1836763

實現裝飾器的需求:

  1. 函數即“變量”,函數名指向內存中的函數體,加()表示調用
  2. 高價函數,將函數當做參數傳遞給其他函數
  3. 嵌套函數,在函數體內再定義函數

1.調用簡單的函數:

import time
def test1():
    time.sleep(1)
    print('in the test1')
test1()

output: 
in the test1

2.在不改變原來函數的定義和調用方式,增加計時功能

import time
def timmer(func):
    def warpper(*args,**kwargs):
        start_time=time.time()
        func()
        stop_time=time.time()
        print('the func run time is %s' %(stop_time-start_time))
    return  warpper

@timmer
def test1():
    time.sleep(1)
    print('in the test1')
test1()

output:
in the test1
the function run time is -1.0007600784301758:
# /usr/bin/env python
# -*- coding: utf-8 -*-
# Author:Jenvid.yang
# def foo():
#     print('in the foo')
#     bar()
# foo()
# bar() 未定義,肯定會報錯

# def bar():
#     print('in the bar')
# def foo():
#     print('in the foo')
#     bar()
# foo()

# def foo():
#     print('in the foo')
#     bar()
# def bar():
#     print('in the bar')
# foo()
# 定義函數的時候,只是用函數名指向內存地址,只要函數執行步驟在函數定義之后即可,各函數間前后關系不影響

# def foo():
#     print('in the foo')
#     bar()
# foo()
# def bar():
#     print('in the bar')
函數名及調用關系

總結1:定義函數的時候,只是用函數名指向內存地址,只要函數執行步驟在函數定義步驟之后即可,各函數間定義先后不影響,與變量定義先后一個道理

 

高階函數

1.把一個函數名當做實參傳給另外一個函數--實現裝飾器:不修改被裝飾函數下,給源代碼,也就是被裝飾函數增加新功能

2.返回值中包含函數名--實現裝飾器:不修改函數的調用方式

  • 函數返回實參函數內存地址
def bar():
print('in the bar')
def test1(func):
print(func)
test1(bar)
print('分割線'.center(30,'-'))
def test2(func):
return func
fff=test2(bar) # 將內存地址,也就是函數體,也就是test2返回值,賦值給一個變量
fff() # 變量加上(),表示調用
output:
<function bar at 0x7f5fa0a7d730>
-------------分割線--------------
in the bar
  • 高階函數傳參方式實現源代碼bar沒有被更改,調用方式被更改了
import time
def bar():
    time.sleep(1)
    print('in the bar')
def test1(func):
    start_time=time.time()
    func()
    stop_time=time.time()
    print('the function run time is %s' % (stop_time-start_time))
test1(bar)
output:
in the bar
the function run time is 1.0011107921600342
#被調用函數原來調用方式是:bar(),函數體里面的內容也沒有更改
#新的函數是test1,調用的是test1(),改變了調用函數名,如果有生產線上已經在調用bar(),這種情況下意味着所有的調用bar函數的其他函數,都需要將bar改成test1
  • 高階函數返回值方式實現函數調用方式不更改
import time
def bar():
time.sleep(1)
print('in the bar')
def test1(func):
start_time=time.time()
return func
stop_time=time.time()
print('the function run time is %s' % (stop_time-start_time))
bar=test1(bar)
bar()
output:
in the bar
# 高階函數返回內存地址實現調用方式不更改,但函數的新功能又沒加上
  • 嵌套函數,在高階函數下,給新功能函數增加多一層函數嵌套
import time
def bar():
    time.sleep(1)
    print('in the bar')
def timmer(func):
    def test1():
        start_time=time.time()
        func()
        stop_time=time.time()
        print('the function run time is %s' % (stop_time-start_time))
    return test1  #返回test1的內存地址
bar=timmer(bar) #將bar函數地址作為實參傳遞給timmer函數的形參func,func將在嵌套函數test1里面進行調用
bar() #此時的bar其實是timmer函數地址,並不是原來的bar
output:
in the bar
the function run time is 1.0010881423950195
  • 語法糖
import time
def timmer(func):
    def test1():
        start_time=time.time()
        func()
        stop_time=time.time()
        print('the function run time is %s' % (stop_time-start_time))
    return test1
# bar=timmer(bar)
@timmer  #只需要在被調用函數的前面用關鍵字符“@裝飾器函數”,既可以實現裝飾器效果
def bar():
    time.sleep(1)
    print('in the bar')
bar()
output:
in the bar
the function run time is 1.0011053085327148
import time
def timmer(func):
    def warpper():  #一般將裝飾器嵌套函數名命名為warpper  據說懂了這個可以解決90%的問題
        start_time=time.time()
        func()
        stop_time=time.time()
        print("the function  run time is %s:" %(start_time-stop_time))
    return warpper
@timmer  #這里還是相當於更改被裝飾函數的’變量名‘內存地址 bar=timmer(bar)
def bar():
    time.sleep(1)
    print('in the bar')
bar()
  • 裝飾器參數傳值-被裝飾函數傳1個值
import time
def timmer(func):
    def warpper(name):
        start_time=time.time()
        func(name)
        stop_time=time.time()
        print("the function  run time is %s:" %(start_time-stop_time))
    return warpper
@timmer
def test1(name):
    time.sleep(1)
    print('in the test1 %s',name)
test1('alex')
output:
in the test1 %s alex
the function  run time is -1.0006732940673828:
  • 裝飾器參數傳值-被裝飾函數傳2個值
import time
def timmer(func):
    def warpper(name,age):
        start_time=time.time()
        func(name,age)
        stop_time=time.time()
        print("the function  run time is %s:" %(start_time-stop_time))
    return warpper
@timmer
def test1(name,age):
    time.sleep(1)
    print('in the test1 name is %s,age is %d'%(name,age))
test1('alex',18)
output:
in the test1 name is alex,age is 18
the function  run time is -1.000108003616333:
  • 裝飾器參數傳值-被裝飾函數傳n個值
import time
def timmer(func):
    # print('func timmer')
    def wrapper(*args,**kwargs):
        print('打印實參:',args,kwargs)
        start_time=time.time()
        func(*args,**kwargs)
        stop_time=time.time()
        print("the function  run time is %s:" % (stop_time-start_time))
    return wrapper
@timmer
# test1=timmer(test1)
def test1(name,age):
    time.sleep(1)
    print('in the test1 func, name is %s,age is %d' % (name,age))
test1('alex',18)

@timmer
# test1=timmer(test1)
def test2(name,age,sex,salary):
    time.sleep(1)
    print('in the test1 func, name is %s,age is %d,sex is %s,salary is %d' % (name,age,sex,salary))
test2('alex',18,'man',3000)
output:
打印實參: ('alex', 18) {}
in the test1 func, name is alex,age is 18
the function  run time is 1.0013768672943115:
打印實參: ('alex', 18, 'man', 3000) {}
in the test1 func, name is alex,age is 18,sex is man,salary is 3000
the function  run time is 1.0012915134429932:
  • 試錯過程:
  • import time
    def timmer(func):
        def warpper():
            # print('打印實參:',args,kwargs)
            start_time=time.time()
            func()
            stop_time=time.time()
            print("the function  run time is %s:" % (stop_time-start_time))
        return warpper
    @timmer #這里相當於test1=timmer(test1)=wrapper,test1加上參數相當於test1(name,age)=wrapper(name,age)
    def test1(name,age):
        time.sleep(1)
        print('in the test1 name is %s,age is %s'%(name,age))
    test1('alex',18)
    輸出如下:
    /usr/bin/python3.5 /root/PycharmProjects/S14/day04/bin/decoreate03.py
    Traceback (most recent call last):
      File "/root/PycharmProjects/S14/day04/bin/decoreate03.py", line 17, in <module>
        test1('alex',18)
    TypeError: warpper() takes 0 positional arguments but 2 were given
    
    Process finished with exit code 1
    step1:定義裝飾器函數體timmer
    step2:語法糖,調用裝飾器函數,准備裝飾下一行函數,返回內層嵌套函數變量warpper內存地址
    step3:定義函數體test1
    step4:調用函數test1,將test1作為實參,傳遞給裝飾器timmer
    step5:warpper函數調用,需要講test1的實參傳遞給wrapper,由於命名空間關系,test1的參數無法傳遞到warpper,func也無法獲得實參
  • 給包裝函數warrper增加形參
  • import time
    def timmer(func):
        def warpper(name,age):
            # print('打印實參:',args,kwargs)
            start_time=time.time()
            func()
            stop_time=time.time()
            print("the function  run time is %s:" % (stop_time-start_time))
        return warpper
    @timmer  #test1=timmer(test1)
    def test1(name,age):
        time.sleep(1)
        print('in the test1 name is %s,age is %s'%(name,age))
    test1('alex',18)
    輸出如下
    /usr/bin/python3.5 /root/PycharmProjects/S14/day04/bin/decoreate03.py
    Traceback (most recent call last):
      File "/root/PycharmProjects/S14/day04/bin/decoreate03.py", line 17, in <module>
        test1('alex',18)
      File "/root/PycharmProjects/S14/day04/bin/decoreate03.py", line 9, in warpper
        func()
    TypeError: test1() missing 2 required positional arguments: 'name' and 'age'
    這個解釋很明顯,func就是test1,缺少了2個位置參數,把func()改成func(name,age)即正確

 

  • # /usr/bin/env python
    # -*- coding: utf-8 -*-
    # Author:Jenvid.yang
    import time
    def timmer(func): #func=test2
        def wrapper(*args,**kwargs):
            start_time=time.time()
            func(*args,**kwargs)
            stop_time=time.time()
            print('func run time is %s' %(stop_time-start_time))
        return wrapper
    
    @timmer
    def bar():
        time.sleep(1)
        print('in the bar')
    bar()
    @timmer  #test2=timmer(test2)=warpper  ->test2(name)=warpper(name)
    def test2(name):
        time.sleep(1)
        print('in the test2',name)
    test2('alex')
    課堂分析源碼
  • 被裝飾函數如果帶有返回值的情況
import time
def timmer(func):
    # print('func timmer')
    def wrapper(*args,**kwargs):
        print('打印實參:',args,kwargs)
        start_time=time.time()
        func(*args,**kwargs)
        stop_time=time.time()
        print("the function  run time is %s:" % (stop_time-start_time))
    return wrapper
@timmer
# test1=timmer(test1)
def test1(name,age,address):
    time.sleep(1)
    print('in the test1 func, name is %s,age is %d,address is %s' % (name,age,address))
test1('alex',18,'nuber001')
# @timmer
# test1=timmer(test1)
def test3(name,age,sex,salary):
    time.sleep(1)
    # print('in the test1 func, name is %s,age is %d,sex is %s,salary is %d' % (name,age,sex,salary))
    return (name,age,sex,salary)
print(test3('alex',18,'man',3000))
輸出如下:
/usr/bin/python3.5 /root/PycharmProjects/S14/day04/decoreate02.py
打印實參: ('alex', 18, 'nuber001') {}
in the test1 func, name is alex,age is 18,address is nuber001
the function  run time is 1.0009291172027588:
('alex', 18, 'man', 3000) Process finished with exit code 0
函數test1,沒有返回值,裝飾后正常輸出
函數test2,帶有返回值,沒有裝飾,打印了實參元組
如果test2裝飾后呢?
import time
def timmer(func):
    # print('func timmer')
    def wrapper(*args,**kwargs):
        print('打印實參:',args,kwargs)
        start_time=time.time()
        func(*args,**kwargs)
        stop_time=time.time()
        print("the function  run time is %s:" % (stop_time-start_time))
    return wrapper
@timmer
# test1=timmer(test1)
def test1(name,age,address):
    time.sleep(1)
    print('in the test1 func, name is %s,age is %d,address is %s' % (name,age,address))
test1('alex',18,'nuber001')
@timmer
# test1=timmer(test1)
def test3(name,age,sex,salary):
    time.sleep(1)
    # print('in the test1 func, name is %s,age is %d,sex is %s,salary is %d' % (name,age,sex,salary))
    return (name,age,sex,salary)
print(test3('alex',18,'man',3000))
輸出如下:
打印實參: ('alex', 18, 'nuber001') {}
in the test1 func, name is alex,age is 18,address is nuber001
the function  run time is 1.0010955333709717:
打印實參: ('alex', 18, 'man', 3000) {}
the function  run time is 1.0010552406311035:
None
帶有返回值的test2被裝飾后,變成了None,改變了原函數的輸出,這是不允許的

給包裝函數中的調用原函數增加賦值和返回值:
import time
def timmer(func):
    # print('func timmer')
    def wrapper(*args,**kwargs):
        print('打印實參:',args,kwargs)
        start_time=time.time()
        res=func(*args,**kwargs)  #用一個變量保存原函數輸出值
        stop_time=time.time()
        print("the function  run time is %s:" % (stop_time-start_time))
        return res #返回原函數結果給wrapper
    return wrapper
@timmer
# test1=timmer(test1)
def test1(name,age,address):
    time.sleep(1)
    print('in the test1 func, name is %s,age is %d,address is %s' % (name,age,address))
test1('alex',18,'nuber001')
@timmer
# test1=timmer(test1)
def test3(name,age,sex,salary):
    time.sleep(1)
    # print('in the test1 func, name is %s,age is %d,sex is %s,salary is %d' % (name,age,sex,salary))
    return (name,age,sex,salary)
print(test3('alex',18,'man',3000))
輸出如下:
打印實參: ('alex', 18, 'nuber001') {}
in the test1 func, name is alex,age is 18,address is nuber001
the function  run time is 1.0010664463043213:
打印實參: ('alex', 18, 'man', 3000) {}
the function  run time is 1.0010428428649902:
('alex', 18, 'man', 3000)
  • 語法糖帶參數情況,index使用本地驗證,home使用ldap驗證,同一個裝飾器,如何實現不同的驗證方式?
  • def deco(func):
        def wrapper(username,password):
            print('I am 包裝1')
            func(username,password)
            print('I am 包裝2')
        return wrapper
    @deco(auth_type='local')
    def index(username,password):
        print('welcome to index page',username)
    index('yzw','secret')
    @deco(auth_type='ldap')
    def home(username,password):
        print('welcome to home page',username)
    home('alex','secret')

     

  • 再嵌套一層函數
  • def auth(auth_type):
        def out_wrapper(func):
            def wrapper(username,password):
                if auth_type == 'local':
                    print('via local certification')
                    print('I am 包裝前')
                    func(username,password)
                    print('I am 包裝后')
                elif auth_type == 'ldap':
                    print('嘻嘻嘻嘻')
                    func(username, password)
                    print('via ldap certfication')
            return wrapper
        return out_wrapper
    @auth(auth_type='local')
    #1.全部不帶參數,相當於index=deco(index)
    #2.被裝飾函數帶參數,相當於index=deco(index)=wrapper ->index(args)=wrapper(args)
    #3.語法糖帶參數,就需要多嵌套一層外層函數,將語法糖的參數傳遞給這一外層函數
    def index(username,password):
        print('welcome to index page <local>',username)
    index('yzw','secret')
    @auth(auth_type='ldap')
    def home(username,password):
        print('welcome to home page <ldap>',username)
    home('alex','secret')
    輸出如下:
    /usr/bin/python3.5 /root/PycharmProjects/S14/day04/bin/decorate04.py
    via local certification
    I am 包裝前
    welcome to index page <local> yzw
    I am 包裝后
    嘻嘻嘻嘻
    welcome to home page <ldap> alex
    via ldap certfication

    Process finished with exit code 0

  • #/usr/bin/env python
    #-*- coding: utf-8 -*-
    #Author:jenvid.yang
    def auth(auth_type):
        def out_wrapper(func):
            def wrapper(*args,**kwargs):
                if auth_type == 'local':
                    print('via local certification')
                    func(*args,**kwargs)
                elif auth_type == 'ldap':
                    print('via ldap certfication')
                    func(*args, **kwargs)
            return wrapper
        return out_wrapper
    @auth(auth_type='local')
    #1.全部不帶參數,相當於index=deco(index)
    #2.被裝飾函數帶參數,相當於index=deco(index)=wrapper ->index(args)=wrapper(args)
    #3.語法糖帶參數,就需要多嵌套一層外層函數,將語法糖的參數傳遞給這一外層函數
    def index(username,password):
        print('welcome to index page <local>',username)
    index('yzw','secret')
    @auth(auth_type='ldap')
    def home(username,password):
        print('welcome to home page <ldap>',username)
    home('alex','secret')
    改成萬能參數
  • # /usr/bin/env python
    # -*- coding: utf-8 -*-
    # Author:Jenvid.yang
    import time
    user,passwd='alex','abc123'
    def auth(auth_type):
        print('auth func:',auth_type)
        def outer_wrapper(func):
            def wrapper(*args,**kwargs):
                print('wrapper func args:',*args,**kwargs)
                if auth_type == 'local':
                    username=input('username:').strip()
                    password=input('password:').strip()
                    if user == username and passwd == password:
                        print('\033[32;1m user has passed authentication \033[0m')
                        res = func(*args,**kwargs)
                        print('xxxxx')
                        return res
                    else:
                        exit('\033[31;1m invalid username or password \033[0m')
                elif auth_type == 'ldap':
                    print('xxxx')
            return wrapper
        return outer_wrapper
    # @auth
    def index():
        print('welcome to index page')
    @auth(auth_type='local')  #home=wrapper()
    def home():
        print('welcom to home page')
        return ('from home')
    # 函數結果被改變
    @auth(auth_type='ldap')
    def bbs():
        print('welcome to bbs page')
    
    index()
    print(home()) # wrapper()
    bbs()
    課堂分析源碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM