1. 程式人生 > 實用技巧 >python筆記(4)---函式(2)

python筆記(4)---函式(2)

函式使用(2)

目錄

Recall

複習一下
[注:]在python中的函式可以當作變數來進行賦值!!!
例子如下:

def fun(name):
    print("I am {}".format(name))
fun("zhenzhen")
f=fun#將函式fun賦值為f
f("xiongxiong")#然後進行呼叫也是可以進行執行的

回想一下上一次所提到的函式中的函式(也就是子函式),看下面這樣的一個例子:

def fun(key):
    def subfun():
        if key=='1':
            print("I am {}".format(key+"號"))
        elif key=='0':
            print("I am {}".format(key+"號"))
    return subfun
f=fun("1")
f()

Question1:上面的寫法是否正確,呼叫了f()之後會發生什麼?

裝飾器

python 的裝飾器是相對於函式而言的,以下網上有一個不友好但是很恰當的比喻如下:
每個人都有的內褲主要功能是用來遮羞,但是到了冬天它沒法為我們防風禦寒,咋辦?我們想到的一個辦法就是把內褲改造一下,讓它變得更厚更長,這樣一來,它不僅有遮羞功能,還能提供保暖,不過有個問題,這個內褲被我們改造成了長褲後,雖然還有遮羞功能,但本質上它不再是一條真正的內褲了。於是聰明的人們發明長褲,在不影響內褲的前提下,直接把長褲套在了內褲外面,這樣內褲還是內褲,有了長褲後寶寶再也不冷了。裝飾器就像我們這裡說的長褲,在不影響內褲作用的前提下,給我們的身子提供了保暖的功效。


python的裝飾器就是形如上面的功能,下面考慮這樣的一個簡單的場景:假定你現在要寫100個函式:

def fun1():
    print("I am duanfeixiong !!!")
    pass
def fun2():
    print("I am duanfeixiong !!!")
    pass
def fun3():
    print("I am duanfeixiong !!!")
    pass
def fun4():
    print("I am duanfeixiong !!!")
    pass
def fun5():
    print("I am duanfeixiong !!!")
    pass
def fun6():
    print("I am duanfeixiong !!!")
    pass
......
def fun100():
    print("I am duanfeixiong !!!")
    pass

上面的100個函式每一個函式都要列印一句相同的字串"I am duanfeixiong !!!",那麼整段程式碼看起來顯得極其的醜陋可讀性差,那麼對於這樣的場景(實際場景往往更加複雜!),就可以使用python的裝飾器的形式,我們寫下這樣的程式碼:
在老一點版本上可以這樣寫:

def summary(func):
    def wrapper():
        print("I am duanfeixiong!!!")
        return func()
    return wrapper
def fun():
    '''
    do something
    '''
    pass

#裝飾部分
fun=summary(fun)
fun()

分析一下上面程式碼的執行過程:
首先我們定義了一個summary函式,傳遞進去的引數是一個函式fun, summary這個函式返回的是wrapper()函式,wrapper函式首先執行列印那句語句然後執行fun()部分,但是這樣寫法有點問題,如果fun函式有引數比如fun(key)怎麼辦,一個寫法是這樣的:

def summary(func):
    def wrapper(key):
        print("I am duanfeixiong!!!")
        return func(key)
    return wrapper
def fun(key):
    '''
    do something
    '''
    print(key)
    pass

#裝飾部分
fun=summary(fun)
fun(key)

回憶一下上次提到的函式的子函式以及子函式作為引數進行返回,觀察上面的方法不難看出,我們只需要把fun的引數移植到wrapper函式的引數那裡就行,這樣根據上面所說的就可以進行對被裝飾的函式的引數傳遞

@語法糖

觀察上面的函式可以瞭解到一個共同點都需要在裝飾的時候使用一條賦值語句這樣有點多餘,並且作用域的使用不好把握,要是在整個程式碼中使用被裝飾的函式的話,你不得不在你的程式碼的最上面使用那條賦值語句,這個時候來了一種新的方式,同樣是上面的程式碼我們只需要如下的寫法即可:

def summary(func):
    def wrapper():
        print("I am duanfeixiong!!!")
        return func()
    return wrapper

@summary #直接在函式宣告的時候對其進行裝飾,和賦值語句是等價的寫法
def fun():
    '''
    do something
    '''
    pass

#裝飾部分
#fun=summary(fun)可以刪除這句話
fun()

生成器和生成函式

重新回到for迴圈的地方談一談基於內容的for迴圈,我們知道要是對一個list進行遍歷,並且每一次拿到list中的值進行列印的話,那麼採用下面的語句是再適合不過的:

L=[1,1,2,2,3,4,5,5]
for d in L:
    print(d)

那麼可以被for迴圈直接作用的容易和資料型別有哪些?

  1. 集合型別的容器,例如 list,tuple,set,str,dict
  2. 生成器生成函式
    可以通過for迴圈直接訪問的叫做可迭代物件

需要記住上面兩種!!!

對於第一種型別而言我們已經很熟悉了,考慮一下第二種的使用:

生成器

建立方式如下:

L=[x**2 for x in range(100)] #這是一個列表解析式,對0-99的數字平方後放在L中

G=(x**2 for x in range(100)) #這是一個生成器不是tuple

闡述一下上面兩種的本質上的區別:

L是一個集合型別的容器list,在初始化的時候已經決定了他的大小是100,而對於G而言並沒有,G是一個生成器需要不斷呼叫一個next函式不斷返回下一個值,對於空間的開銷極其低相比於list

>next(G)
0
>next(G)
1
>next(G)
4
....

也可以被for迴圈直接作用:

for d in G:
    print(d)

上面的for迴圈等價於對G不斷的使用next()函式

生成函式

依舊是上面的例子和函式$x2$那麼如果我們想寫一個函式接受一個引數n返回從0到n-1的所有x2的值,那麼我們可以這樣寫:

def fun(n):
    L=[]
    for i in range(n):
        L.append(i**2)
    return L

但是這樣的寫法太過於繁瑣,list對空間的開銷比較大,我們可以轉而使用生成函式

def fun(n):
    for i in range(n):
        yield i**2
fun(10)#建立了一個生成器
print(type(fun(10)))#檢視型別 自己列印一下!!!
next(fun(10))#next函式照樣適用
#也可以
for d in fun(10):
    print(d)

yield關鍵字可以這樣理解,在呼叫了next()函式之後,fun(10)中for迴圈前進一次,yield可以當作返回值,不同於別的普通函式的是:next()下一次的時候從上一次next()函式終止的位置繼續往後執行!!!