1. 程式人生 > 實用技巧 >Docker(三):容器太多,操作好麻煩

Docker(三):容器太多,操作好麻煩

一、閉包函式

閉包函式=函式巢狀定義+函式物件+名稱空間與作用域

閉包函式:在函式中(巢狀)定義另一個函式時,如果內部的函式引用了外部的函式的變數,則可能產生閉包。

1、閉:指的是該函式是定義在一個函式內部的函式
2、包:值得是該函式訪問了一個來自於外層函式的變數

為函式體傳參的方法:

'''方案一:直接使用引數的形式傳遞'''
def wrapper(x):
    print(x)

wrapper(111)
wrapper(222)
'''方案二:把函式體想要的引數包給它(即使用閉包的概念)'''
def outter(x):
    def wrapper():  # wrapper = 閉包函式的記憶體地址
        print(x)

    return wrapper  # 一定不要加括號

f1 = outter(111)  # f = 閉包函式的記憶體地址
f2 = outter(222)  # f = 閉包函式的記憶體地址
f1()
f2()
-----------
111
222

乍一看會感覺使用閉包來傳引數非常的麻煩,我們之前使用函式,需要引數都是直接傳給他,方便也快捷,但是在某些場景下我們定死了某個函式無法直接傳引數,那就必須通過其他方式傳引數,即閉包的方式,下面介紹的裝飾器就是閉包的使用。

二、@符號的使用

@符號作用: 
(1) 可以自動把@符號下面的函式當成引數傳遞給裝飾器
(2) 把新函式進行返回,讓新函式去替換舊函式,以實現功能的擴充套件.
如:
@timer  # 等於func1 = timer(func1),其目的是為了讓使用者不改變呼叫方式,它依然在呼叫func1,但是實際已經被我們換成了timer
def func1():
  print(1)

三、疊加多個裝飾器的執行步驟

結論:(記住結論既可)

載入順序:自下而上
執行順序:自上而下執行內層的wrapper函式

驗證過程:

# 疊加多個裝飾器
def deco1(func1):  # func1 = wrapper2的記憶體地址
    def wrapper1(*args,**kwargs):
        print('wrapper1====>')
        res1=func1(*args,**kwargs)
        return res1
    return wrapper1

def deco2(func2):  # func2 = wrapper3的記憶體地址
    def wrapper2(*args,**kwargs):
        print('wrapper2====>')
        res2=func2(*args,**kwargs)
        return res2
    return wrapper2

def deco3(func3):  # func3 = 最原始的那個被裝飾函式的記憶體地址
    def wrapper3(*args,**kwargs):
        print('wrapper3====>')
        res3=func3(*args,**kwargs)
        return res3
    return wrapper3

        # index=wrapper1的記憶體地址
@deco1  # deco1(wrapper2的記憶體地址)=>wrapper1的記憶體地址
@deco2  # deco2(wrapper3的記憶體地址)=>wrapper2的記憶體地址
@deco3  # deco3(最原始的那個被裝飾函式的記憶體地址)=>wrapper3的記憶體地址  
def index(x,y):
    print('index=>',x,y)

index(1,2)
-----------------------
wrapper1====>'
wrapper2====>
wrapper3====>
index=>1,2

四、無參裝飾器

什麼是裝飾器:

器:工具
裝飾:為被裝飾者新增額外的功能

為何要有裝飾器:

軟體一旦上線執行之後,就應該遵循開放封閉原則:
1、開放指的是對拓展新功能開放
2、封閉指的是對修改原始碼封閉
這種情況下,我們在寫新功能時,若需要用到新的引數,就無法直接通過原函式來傳了,必須通過其他方式來傳參,目前我們想到的方法,是兩種傳參的另一種的方式,閉包函式

定義裝飾器的目的:
定義裝飾器就是為了在遵循1和2的前提下來為其他函式新增新功能的

ps:
不修改被裝飾物件指的是定義與呼叫都不能修改
所以下述行為都違反了開放封閉原則:
①、修改被裝飾物件定義時的原始碼
②、修改被裝飾物件的呼叫方式

如何用裝飾器:

'''無參裝飾器基本格式'''
def wrapper(func):
    def inner(*args,**kwargs):
        res = func(*args,**kwargs)
        return func
    return inner 
  '''
  切記不能在此處加括號,加括號就代表執行,相當於先把inner執行一遍,然後拿到返回值return出去.
  return 出去的值是無法加括號在執行的。
  '''
# 例子
def wrapper(func):
    def inner(*args,**kwargs):
        res = func(*args,**kwargs)
        return res
    return inner()  # 切記不能在此處加括號,加括號就代表執行

@wrapper
def func1():
    print(11)

func1()
-----------------
11
TypeError: 'NoneType' object is not callable

'''
前面打印出來的11其實是執行wrapper裡的inner產生的,所以以上程式的執行流程是
1.先把func1傳給wrapper,然後運行了wrapper中的程式碼,返回 inner()
2.inner() 就開始執行inner函式,即運行了一遍func1,所以列印了11
3.此時的func1 = inner()  而inner()又等於func1(),func1沒有返回值,預設返回None
4.變成了None(),報錯
'''

無參裝飾器的應用場景以及構建裝飾器步驟:

需求:有一個index函式,此時我們要在index函式基礎上新增一個計算運算時間的功能,因為是公司專案,我們不能修改原函式的呼叫方式,也不能修改原始碼。

# 一、被裝飾器物件index如下
import time

def index(x,y):
    time.sleep(1)
    print("index---->",x,y)

index(1,2)

# 二、為index新增計算運算時間與登入功能
import time
from functools import wraps

def timmer(func):
    def wrapper(*args,**kwargs):
        start = time.time()
        res = func(*args,**kwargs)
        end = time.time()
        print(end-start)
        return res
    return wrapper

def login(func):
    def wrapper(*args,**kwargs):
        name = 'yang'
        pwd = '123'
        inp_name = input("請輸入您的使用者名稱:").strip()
        inp_pwd = input("請輸入您的密碼").strip()
        if inp_name == name and inp_pwd == pwd:
            print("登入成功")
            res = func(*args,**kwargs)
            return res
    return wrapper

@timmer
@login
def index(x,y):
    time.sleep(1)
    print("index---->",x,y)

index(1,2)

五、有參裝飾器

對於不再需要新引數的裝飾器,兩層就可以解決了,但是當我們的裝飾器需要新的引數,如在登入的時候,我們要判斷我們使用者名稱與密碼的來源,此時需要外部將來源傳進來。而兩層的裝飾器中,第一層,是為了給原函式傳引數,他的引數不能動,而第二層,因為裝飾器語法@的原因,也已經定死了此處只能傳一個函式名,也不能傳引數,所以我們需要構造第三層來接受外部的引數,而在內部的函式可以在不改動自己依然可以獲取到外層的函式.

'''有參裝飾器模版'''
def outter2(x,y,z,a,b):
    def outter1(func):
        def wrapper(*args,**kwargs):
            res=func(*args,**kwargs)
            return res
        return wrapper
    return outter1

例子:

def outter(engine = 'file'):
    def deco2(func2):
        def wrapper2(*args,**kwargs):
            inp_name = input('username>>>: ').strip()
            inp_pwd = input('password>>>: ').strip()

            if engine == "file":
                print('基於file的認證')
                if inp_name == "egon" and inp_pwd == "123":
                    print('login successful')
                    res2=func2(*args,**kwargs)
                    return res2
                else:
                    print('username or password error')
            elif engine == 'mysql':
                print('基於mysql的認證')
            elif engine == 'ldap':
                print('基於ldap的認證')
            else:
                print('未知的engine')
        return wrapper2
    return deco2

@outter(engine='mysql')  # @deco2 # index=deco2(index)
def index(x,y):
    print('index=>',x,y)

index(1,2)  # index=>wrapper

六、類裝飾器

我們也可以用類來裝飾函式

# 利用類靜態方法裝飾
class MyClass:
    @staticmethod
    def zhuangshi1(func):
        def newfunc():
            print("裝飾前")
            func()
            print("裝飾後")
        return newfunc

    def __call__(self, *args, **kwargs):
        return self.zhuangshi2(*args, **kwargs)

    def zhuangshi2(self,func):
        def newfunc():
            print('裝飾前')
            func()
            print('裝飾後')
        return newfunc


# 利用類靜態方法,本質和原來的用函式裝飾一樣
@MyClass.zhuangshi1
def func1():
    print('執行func1')

func1()

# 利用__call__方法
@MyClass()
def func2():
    print('運行了func2')

func2()