1. 程式人生 > 實用技巧 >使用mysql docker官方映象部署mysql

使用mysql docker官方映象部署mysql

目錄

1.函數語言程式設計

雖然面向物件程式設計屬於主流操作,但是不可否認的是包括面向過程、函數語言程式設計、面向切面程式設計等都有著非常優秀的應用。

函數語言程式設計是一種程式設計的正規化,指導我們應該如何去編寫符合函數語言程式設計規範的程式碼,函數語言程式設計具有非常明顯的五個特點:

1.1 函式是“一等公民”

所謂的一等公民就是將函式與其他的資料型別一樣處於一個平等的地位,可以將函式賦值給變數,可以作為引數傳遞給其他函式,也能夠作為返回值返回。

1.2 只用“表示式”,不用“語句”

“表示式”是一個單純的運算過程,總是有返回值的;“語句”是執行某種操作,沒有返回值。函數語言程式設計要求,只使用表示式,不使用語句。也就是說,每一步都是單純的運算,而且都有返回值。

原因是函數語言程式設計的開發動機,一開始就是為了處理運算,不考慮系統的讀寫。“語句“屬於對系統的讀寫操作,所以就排斥在外。

當然,實際開發中,不做I/O是不可能的,因此,程式設計過程中,函數語言程式設計只要求把I/O限制到最小,不要有不必要的讀寫行為,保持計算過程的單純性。

1.3 沒有”副作用“

所謂的”副作用“,指的是函式內部與外部的互動(最典型的情況,就是修改全域性變數的值),產生運算以外的其他結果。

函數語言程式設計強調沒有”副作用“,意味著函式要保持獨立,所有功能就是返回一個新的值,沒有其他行為,尤其是不得修改外部變數的值。

1.4. 不修改狀態

上一點已經提到了,函數語言程式設計只是返回新的值,不修改系統變數。因此,不修改變數,也是它的一個重要特點。在python中,裝飾器最 常被提到的有點就是在不修改原有程式碼的基礎上為其新增新的功能。

在其他型別的語言中,變數往往用來儲存狀態。不修改變數,意味著狀態不能儲存在變數中。函數語言程式設計使用引數儲存狀態,最好的例子就是遞迴。

由於使用了遞迴,函式式語言的執行速度比較慢,這是它長期不能在業界推廣的主要原因。

1.5引用透明

引用透明,指的是函式的執行不依賴於外部變數或者”狀態“,只依賴於輸入的引數,任何時候只要引數相同,引用函式所得到的返回值總是相同的。

有了前面的第三點和第四點,這點是很顯然的。其他型別的語言,函式的返回值往往與系統狀態有關,不同的狀態之下,返回值是不一樣的。這就叫”引用不透明“,很不利於觀察和理解程式的行為。

2.閉包

都已經提到函數語言程式設計了,那麼閉包也是一個必不可少的需要了解的概念。

2.1 什麼是閉包

python中的閉包可以理解為:如果在一個內部函式裡,對其外部作用域(但不是全域性作用域)的變數進行了引用,那麼內部函式就被認為是閉包。

2.2作用域

作用域指的就是程式執行時變數可以被訪問的範圍,但是閉包函式的作用域和我們常編寫程式碼的作用域就有所不同,閉包就是一種能夠在脫離了函式本身的作用範圍,依舊能夠訪問函式裡的區域性變數的方式。

def func():
    msg = 'test'
    def printer():
        print(msg)
    return printer

func()()

在上述程式碼中,我們將內部函式返回之後,就可以視作msg變數已經脫離了其作用域,但是我們執行func函式的返回結果時,依舊能夠正常打印出變數的資訊,這就是閉包。

2.3 為什麼要使用閉包

因為閉包避免了必須要使用全域性變數的情況,在實際開發的時候,我們應該儘量避免使用全域性變數,而閉包就解決了這個問題,將變數和環境關聯了起來,在這一點上閉包和麵向物件是非常相似的(我看到過的一個對面向物件程式設計一個印象非常深刻的解釋就是將變數和方法繫結)。

def adder(x):
    def wrapper(y):
        return x + y
    return wrapper

adder5 = adder(5)
# 輸出 15
adder5(10)
# 輸出 11
adder5(6)

2.4閉包中使用的變數都存在哪裡了?

看完上述部分,可能會產生疑惑,既然變數已經脫離了其作用域,那麼是如何訪問到內部函式的變數的呢?

這是因為所有的函式都有一個__closure__ 屬性,如果這個函式是一個閉包的話,那麼它返回的是一個由cell物件組成的元組物件。cell物件中的cell_contents屬性就是閉包中的自由變數。

print(adder5.__closure__)
# 輸出 (<cell at 0x0000023315310108: int object at 0x00007FFCF37BD4A0>,)
print(adder5.__closure__[0].cell_contents)
# 輸出 5

2.5 使用閉包時的注意事項

使用閉包常遇到的問題有兩個,第一個就是在內部函式中不能夠修改外部變數,另一個就是在使用高階函式時經常會遇到的問題閉包延時繫結

def func():
    x = 1
    def wrapper():
        x += 1
    return wrapper
func()()
# 直接報錯 UnboundLocalError: local variable 'x' referenced before assignment

解決這個問題的辦法也很簡單,只需在wrapper函式內部用nonlocal修飾引用的外部變數即可。

l = []
for i in range(3):
    def wrapper():
        print(i)
    l.append(wrapper)

for each in l:
    each()
    # 輸出 2 2 2

這個例項是一個比較直觀的例子,在python的幾個高階函式中都有對引數的迭代,如果使用不慎也會輕易就引起上述問題。原因在於,將內部函式新增到列表中的時候,並未對變數的值進行繫結,當執行內部函式的時候才回去尋找變數的值,而變數 i 還沒有被銷燬,其值是迭代的最後一個值,因此結果就和預期要求不一致。解決辦法同樣很簡單:

l = []
for i in range(3):
    def wrapper(x=i):
        print(x)
    l.append(wrapper)

for each in l:
    each()
    # 輸出0 1 2

3.裝飾器

其實在前文中已經完成了一個裝飾器,只是這和我們常用的裝飾器有所區別的是,python中的@語法糖可以簡化裝飾器的呼叫。

3.1多個裝飾器的執行順序

def dec1(func):
    print('這是裝飾器1的外部函式')
    def wrapper(*args, **kwargs):
        print('這是裝飾器1的內部函式')
        return func(*args, **kwargs)
    return wrapper

def dec2(func):
    print('這是裝飾器2的外部函式')
    def wrapper(*args, **kwargs):
        print('這是裝飾器2的內部函式')
        return func(*args, **kwargs)
    return wrapper

@dec1
@dec2
def test():
    print('這裡是test函式')

test()
# 執行結果:
# 這是裝飾器2的外部函式
# 這是裝飾器1的外部函式
# 這是裝飾器1的內部函式
# 這是裝飾器2的內部函式
# 這裡是test函式

可以從結果上看出,多個裝飾器裝飾時,包裝是自內向外,執行是自外向內。

我們將上述程式碼稍作修改就能夠很清晰的明白為什麼它的執行結果會是這個樣子

def dec1(func):
    print('這是裝飾器1的外部函式')
    def wrapper(*args, **kwargs):
        print('這是裝飾器1的內部函式')
        return func(*args, **kwargs)
    return wrapper

def dec2(func):
    print('這是裝飾器2的外部函式')
    def wrapper(*args, **kwargs):
        print('這是裝飾器2的內部函式')
        return func(*args, **kwargs)
    return wrapper

# @dec1
# @dec2
def test():
    print('這裡是test函式')

# test()


test1 = dec2(test)
test2 = dec1(test1)
print('裝飾部分')
test2()
# 執行結果
# 這是裝飾器2的外部函式
# 這是裝飾器1的外部函式
# 裝飾部分
# 這是裝飾器1的內部函式
# 這是裝飾器2的內部函式
# 這裡是test函式

這裡就與本文的函數語言程式設計對應上了,函式也是一等公民,可以作為引數傳遞,可以作為返回值返回,上述程式碼中我們只需要明確一點函式與函式執行是不一樣的就能夠清晰的看出多個裝飾器執行時結果。

可以根據前文中的閉包得出,最後的包裝之後的結果應該是這樣的。

def last(func):
    print('這是裝飾器1的內部函式')
    def wrapper(func):
        print('這是裝飾器2的內部函式')
        return func
    return wrapper(func)

last(test)()
# 執行結果
# 這是裝飾器1的內部函式
# 這是裝飾器2的內部函式
# 這裡是test函式

3.2 裝飾器的執行時機

裝飾器的執行時機可以從前文中得出一個結論就是,裝飾器在裝飾函式時就已經被執行了,在執行被裝飾的函式時並不是去執行裝飾器,而是執行被裝飾器裝飾之後的函式。

還有另外一點就是,被裝飾函式在被匯入另外一個檔案時,裝飾器立刻執行。

這兩點驗證都很簡單,因此不在此演示程式碼。

3.3帶引數的裝飾器

def dec_argument(msg):
    msg = 'test: '+ msg
    def dec1(func):
        def wrapper(*agrs, **kwargs):
            return func(msg, *agrs, **kwargs)
        return wrapper
    return dec1
@dec_argument('帶引數的裝飾器')
def test(msg):
    print(msg)

test()
# 輸出結果
# test: 帶引數的裝飾器

如果看懂了前文的內容的話就會發現這個非常容易理解,就是多一層內部函式巢狀而已。

3.4裝飾器帶來的問題

如果閱讀前文時細心一點就能夠發現裝飾器將給我們帶來什麼樣的問題,在3.1多個裝飾器的執行順序最後得到的last函式那裡就能夠很清晰的看出,被裝飾之後的函式和裝飾之前的函式已經不是同一個函數了,因此原函式的一些資訊也隨之發生了改變:

def dec(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper
@dec # 註釋這一行比較裝飾前後的結果
def test():
    pass
print(test.__name__)
# 裝飾前:test
# 裝飾後:wrapper

可以看到前後結果已經完全不一致了,在python的functools模組中,提供了一個wraps方法可以將原函式的這些資訊複製給被裝飾的函式:

from functools import wraps
def dec(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper
@dec
def test():
    pass
print(test.__name__)
# 裝飾前:test
# 裝飾後:test

文中如有錯誤,請給我留言,我會及時更改,如果對您造成誤導,非常抱歉。

refer:

函數語言程式設計初探:https://www.ruanyifeng.com/blog/2012/04/functional_programming.html

一步一步教你認識python的閉包:https://foofish.net/python-closure.html

python中的閉包:https://blog.csdn.net/marty_fu/article/details/7679297