Python-閉包與裝飾器

閉包函數

閉包函數自己是一個函數,是把閉包函數內部的函數 + 函數內部的變量包裹起來,而後經過返回值 返回出去。閉包

閉包函數必須得符合函數嵌套app

# 閉包函數:自己是一個函數,讓其調用更方便
# 定義
# 把一個函數名 + 變量 封裝成一個函數  返回(return)出去

def out_fun(url):
    # url = "qinyj"
    def iner_fun():
        print(url)
    return iner_fun

a = out_fun("url")
a()

如今有一個函數,老闆有一個需求,想在這個函數上加一個計時功能,就是計算這個函數執行時間,需求吩咐下去,某某公司的3個運維開發開始思考了。。。運維

def index():
    print(from index)

第一我的 小青開始作。。。他的方式是修改源代碼:函數

import time
def index():
    start = time.time()
    print("from index")
    time.sleep(1)
    end = time.time()
    print(end - start)

index()
from index
1.0080575942993164

第二我的 小杰開始作。。。他的方式是在調用的時候計算時間學習

import time
def index():
    print("from index")
    time.sleep(1)

start = time.time()
index()
end = time.time()
print(end - start)
from index
1.0000572204589844

第三我的 小強開始作。。。他用了一個很是牛逼的看不懂的騷操做。。url

import time

def index():
    print("from index")

def time_count(func):
    def inner():
        start = time.time()
        func()
        time.sleep(1)
        end = time.time()
        print(end - start)
    return inner

index = time_count(index)
index()
from index
1.0000572204589844

好了,如今三我的已經所有作完,老闆該驗收了,把他們三我的叫到會議室。。。code

指明瞭小強作的很是不錯!另外兩個向他學習,小強作的方式就是裝飾器ip

什麼叫裝飾器?

裝飾器:自己是一個函數,給已有的函數加功能utf-8

原則:

不改變源代碼

不改變其調用方式

剛纔只有小強的代碼符合上面規定

  1. 裝飾器自己是函數,只不過它用來裝飾 被裝飾的函數
  2. 裝飾器裝飾函數 不改變被裝飾函數源代碼
  3. 裝飾器裝飾函數 不改變被裝飾函數的調用方式

實現裝飾器

# v1   實現最簡單的裝飾器

import time

def index():
    """被裝飾的函數"""
    print('index')
    time.sleep(1)

# time_count裝飾器:對被裝飾函數計時
def time_count(func):  # func纔是真正的index
    """裝飾器"""

    def wrapper():
        start = time.time()
        func()
        end = time.time()
        print(end - start)

    return wrapper

index = time_count(index)  # index == wrapper
index()  # wrapper()
# v2:帶返回值

import time

def index():
    """被裝飾的函數"""
    print('x',x)
    print('index')
    time.sleep(1)
    return 'index'

# time_count裝飾器:對被裝飾函數計時
def time_count(func):  # func纔是真正的index
    """裝飾器"""

    def wrapper():
        start = time.time()
        res = func()  # index()
        end = time.time()
        print(end - start)

        return res

    return wrapper

index = time_count(index)  # index == wrapper
res = index()  # wrapper()
print(res)
# v3:加參數

import time


def index(x,y,z=10):
    """被裝飾的函數"""
    print('x',x)
    print('index')
    time.sleep(1)

    return 'index'


# time_count裝飾器:對被裝飾函數計時
def time_count(func):  # func纔是真正的index
    """裝飾器"""

    def wrapper(*args,**kwargs):  # (10, 20)  # *args和**kwargs接收了全部的參數
        start = time.time()
        res = func(*args,**kwargs)  # index()  # *(10,20)  # *args和**kwargs打散參數傳給真正的index
        end = time.time()
        print(end - start)

        return res

    return wrapper


index = time_count(index)  # index == wrapper
res = index(10,20,320)  # wrapper()
print(res)

以上均爲二層裝飾器:

  1. 用來裝飾函數的,它本質是函數

  2. 不改變函數源代碼

  3. 不改變函數調用方式

若是實在理解不了,就記住裝飾器模板:

from functools import wraps

def deco(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        # 要加什麼功能就加上去
        res = func(*args, **kwargs)

        return res

    return wrapper

這裏提到了一個wraps,其實Python裝飾器(decorator)在實現的時候,被裝飾後的函數其實已是另一個函數了(函數名等函數屬性會發生改變),爲了避免影響,Python的functools包中提供了一個叫wraps的decorator來消除這樣的反作用。寫一個decorator的時候,最好在實現以前加上functools的wrap,它能保留原有函數的名稱和docstring。
廢話很少說,上倆例子就能搞明白!
實例一:
不加wraps

# -*- coding=utf-8 -*- 
from functools import wraps   
def my_decorator(func):
    def wrapper(*args, **kwargs):
        '''decorator'''
        print('Calling decorated function...')
        return func(*args, **kwargs)
    return wrapper  
 
@my_decorator 
def example():
    """Docstring""" 
    print('Called example function')
print(example.__name__, example.__doc__)

# 執行結果:
# ('wrapper', 'decorator')

實例二:
加wraps

# -*- coding=utf-8 -*- 
from functools import wraps   
def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        '''decorator'''
        print('Calling decorated function...')
        return func(*args, **kwargs)
    return wrapper  
 
@my_decorator 
def example():
    """Docstring""" 
    print('Called example function')
print(example.__name__, example.__doc__)

# 執行結果:
# ('example', 'Docstring')

warps 做用: 消除或修復(被裝飾後的函數名等屬性的改變)反作用

裝飾器語法糖

在被裝飾的函數上 加上 @裝飾器函數名,目的是爲了讓代碼更加簡潔。

@time_count
def index(x,y,z=10):
    """被裝飾的函數"""
    print('x',x)
    print('index')
    time.sleep(1)

    return 'index'

三層裝飾器

三層裝飾器就是給裝飾器加參數,在原有的兩層裝飾器上再嵌套一層函數,利用閉包的思想,把函數變量和函數名返回出去。

username_list = []


def sanceng(role):
    def login_deco(func):
        def wrapper(*args, **kwargs):

            if username_list:
                print('已經登陸,請勿重複登陸')
                res = func(*args, **kwargs)
                return res

            username_inp = input('請輸入用戶名:')
            pwd_inp = input('請輸入密碼:')

            with open(f'{role}_info.txt', 'r', encoding='utf8') as fr:
                for user_info in fr:
                    username, pwd = user_info.strip().split(':')
                    if username_inp == username and pwd_inp == pwd:
                        print('登陸成功')
                        username_list.append(username)

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

                else:
                    print('登陸失敗')

        return wrapper

    return login_deco

@sanceng('admin')
def index(x, y):
    print('index')
    print('x,y', x, y)

    return 123

res = index(10, 20)
print(res)
相關文章
相關標籤/搜索