1. 程式人生 > >day047函式之裝飾器(閉包的運用),面向物件之單例模式(設計模式)

day047函式之裝飾器(閉包的運用),面向物件之單例模式(設計模式)

本節內容:

1、函式之裝飾器
2、面向物件之單例模式

一、函式之裝飾器(閉包的運用)

裝飾器本質上就是一個python函式,一個閉包函式的運用,
他可以讓其他函式在不需要做任何程式碼變動的前提下,增加額外的功能,裝飾器的返回值也是一個函式物件。

裝飾器的應用場景:比如插入日誌,效能測試,事務處理,快取等等場景。

1、裝飾器的形成過程

現在有一個需求,要測試一個函式的執行時間,在不改變這個函式的執行情況下:

fe1:簡單版的裝飾器

簡單版的裝飾器

# 增加一個函式 測試其他函式執行效率的功能,但是儘量不要改變原函式的執行方式.

import time

def func(): # 被裝飾的函式 time.sleep(0.2) print('10萬行程式碼') def func1(): time.sleep(0.2) print('11萬行程式碼') # func = 666 # func() def timmer(f): # func函式名 = f def inner(): start_time = time.time() f() end_time = time.time() print('此函式的執行效率為%s' % (end_time - start_time)) return inner func = timmer(func) # inner func() # inner() func1 = timmer(func1) func1() # 上個版本不行,每次執行被測試時間的函式時,必須要加一行程式碼. # 怎麼解決,看下面的語法糖 
Python Copy

fe2:裝飾器—-語法糖

 裝飾器:在不改變原函式的執行(程式碼機構基礎上,給原函式增加一些額外的功能,登入驗證,列印日誌,測試效率等等.
 語法糖,就是python裡面封裝好的func = timmer(func),你直接呼叫就行,不用再自己寫了

裝飾器的語法糖運用

import time
def timmer(f): # func函式名 = f def inner(x,y): start_time = time.time() f(x,y) end_time = time.time() print('此函式的執行效率為%s' % (end_time - start_time)) return inner @timmer # func = timmer(func) def func(x,y): time.sleep(0.2) print(x + y) # 語法糖 @ # @timmer # func1 = timmer(func1) # def func1(): # print(666) func(10,20) # inner(10,20) # func1() 
Python Copy

fe3:被裝飾函式帶引數,這時用無敵傳參(*args, **kwargs)

無敵傳參解決被裝飾函式帶引數

import time
def timmer(f): # func函式名 = f def inner(*args, **kwargs): # 傳參,聚合 # args = (10,20) start_time = time.time() f(*args,**kwargs) # 被裝飾函式的執行 * 打散 10 , 20 f(10,20) end_time = time.time() print('此函式的執行效率為%s' % (end_time - start_time)) return inner @timmer # func = timmer(func) func2 = timmer(func2) 這時的func2已經不是被裝飾函數了 def func(x,y): time.sleep(0.2) print(x + y) @timmer def func2(x,y,z,w): print(x,y,z,w) # func(10, 20) # inner(10,20) func2(1,2,3,w=4) # 這裡的引數被inner的f接收了,f就是語法糖傳參的被裝飾函式名 
Python Copy

fe4: 標準的裝飾器, 被裝飾的函式帶引數,帶返回值

標準的裝飾器

import time
def timmer(f): # func函式名 = f def inner(*args, **kwargs): # 接收被裝飾函式的引數 start_time = time.time() ret = f(*args,**kwargs) # 接收被裝飾函式執行之後的返回值 end_time = time.time() print('此函式的執行效率為%s' % (end_time - start_time)) return ret # 將返回值,返回給呼叫者 return inner @timmer # func = timmer(func) def func(x,y,z,e): time.sleep(0.2) return x + y + z + e ret1 = func(10, 30, 3, 4) # inner(10, 30) print(ret1) 
Python Copy

2、面試題(手寫一個裝飾器)

def wrapper(f): def inner(*args, **kwargs): '''被裝飾函式執行之前的操作''' ret = f(*args, **kwargs) """被裝飾函式執行之後的操作""" return ret return inner 
Python Copy

3、裝飾器的應用: 登入驗證.

需求:登陸後可以在各個頁面切換

名字

dic_status = {
    'username': None, 'status': False, } def login(f): def inner(*args, **kwargs): '''被裝飾函式執行之前的操作''' if dic_status['status'] : ret = f(*args, **kwargs) return ret else: username = input('請輸入使用者名稱').strip() password = input('請輸入密碼').strip() with open('register', encoding='utf-8') as f1: for line in f1: user, pwd = line.split('|') if username == user.strip() and password == pwd.strip(): dic_status['username'] = username dic_status['status'] = True print('登入成功,正在跳轉...') ret = f(*args, **kwargs) return ret else: print('登入失敗...') return inner @login def article(): print('歡迎登入文章頁面') @login def diary(): print('歡迎登入日記頁面') @login def comment(): print('歡迎登入評論頁面') dic = { 1: article, 2: diary, 3: comment, } while 1: num = input('歡迎訪問部落格園,請選擇:\n1文章頁面 \n2 日記頁面 \n 3 評論頁面').strip() if num.isdigit(): dic[int(num)]() else: print('重新輸入..') # 還需寫一個register的檔案來放使用者資訊,進行登入驗證 
Python Copy

4、帶引數的裝飾器

帶引數的裝飾器

import time
def timmer(flag): # 這個函式的作用是,將引數傳給裝飾器函式 def wrapper(f): # 這個函式的作用是:將被裝飾函式作為引數傳入 def inner(*args, **kwargs): # 傳入被裝飾函式的引數,傳參 if flag: # 通過這個狀態來,控制是否執行裝飾器函式 start_time = time.time() ret = f(*args, **kwargs) end_time = time.time() print('執行時間%s' % (end_time-start_time)) return ret else: ret = f(*args, **kwargs) return ret return inner return wrapper flag1 = False @timmer(flag1) # 第一步,將@ 與後面 分開,只是單純的執行timmer(flag1)函式 第二部 將@ 與 wrapper相結合,形成裝飾器 def func1(): time.sleep(0.3) print('in func1') func1() 
Python Copy

5、多個裝飾器 裝飾一個函式

多個裝飾器 裝飾一個函式

def wrapper1(func): # func == f 函式名 def inner1(): print('wrapper1 ,before func') # 2 func() print('wrapper1 ,after func') # 4 return inner1 def wrapper2(func): # func == inner1 def inner2(): print('wrapper2 ,before func') # 1 func() print('wrapper2 ,after func') # 5 return inner2 # 就近原則 先將近的傳給裝飾器函式,多個裝飾器的話,逐層上傳 @wrapper2 # f = wrapper2(f) 裡面f == inner1 外面的f == inner2 @wrapper1 # f = wrapper1(f) 裡面的f == 函式名f 外面的f == inner1 def f(): print('in f') # 3 f() # inner2() 
Python Copy

二、面向物件之單例模式

就是隻能例項化一個物件,不管建立多少個物件,都是指向同一個記憶體地址

例如:多個py檔案在引入同一個模組,如果沒有設定

1、單例模式程式碼示例

class A:
    __instance = None # 定義一個私有變數, def __new__(cls, *args, **kwargs): # 通過重寫父類的__new__方法,來控制指向第一個例項化物件的地址 if A.__instance is None: # 通過私有變數來控制指向第一個例項化物件的(同一個)的地址 obj = object.__new__(cls) # 如果是第一次例項化物件,就建立物件, A.__instance = obj return A.__instance # 如果不是第一次,就直接指向第一次物件的記憶體地址,不再建立物件 obj1 = A() obj2 = A() obj3 = A() obj4 = A() print(obj1) # 都指向同一個記憶體地址 print(obj2) print(obj3) print(obj4)