1. 程式人生 > >這篇部落格記錄廖雪峰python教程的習題(二)

這篇部落格記錄廖雪峰python教程的習題(二)

函數語言程式設計

高階函式—–sorted

首先我們需要明確一點就是sorted()函式是作用於一個列表,對列表中的每一項元素進行排序,因為sorted本身的作用就是對元素排序,如果後面還有key=function ,則是先對列表中的每一項元素按照function進行作用,sorted()函式在對返回的結果(仍然是存在一個list中)在進行排序!理解了上面的邏輯我們就可以做題啦。

“假設我們用一組tuple表示學生名字和成績:
L = [(‘Bob’, 75), (‘Adam’, 92), (‘Bart’, 66), (‘Lisa’, 88)]
請用sorted()對上述列表分別按名字排序:”

程式碼如下:

#請用sorted()對上述列表分別按名字排序:
def main():
    L=[('Bob',98),('xuanxuan',90),('hehe',97),['Asas',100]]
    L1=sorted(L,key=sort_byname)
    print(L1)


def sort_byname(t):  #t這裡代表著一個元組tuple因為list中每一項不再是單純的一個元素,而是一個tuple
    return t[0].lower()
    #return t[0]

main()

需要注意的是這裡sort_byname()函式的作用物件是sorted()函式中的list的每一項,在這裡也就是一個tuple了,由於我們是希望對名字進行排序,sort_byname()函式只需要對一個tuple返回第一個元素值 也就是名字 ,然後sorted()函式會對返回來的值 再進行排序,自然就是按名字排序了

如果是按照分數進行排序呢:

#還是上面那個例子,請用sorted()對上述列表按分數進行排序
def main():
    L=[('Bob',98),('xuanxuan',90),('hehe',97),['Asas',100]]
    L1=sorted(L,key=sort_byscore)
    print(L1)

def sort_byscore(t):
    return t[1]

main()

返回函式

需要注意的是,如果一個函式的返回值仍然是一個函式或者函式的某種形式(比如說列表中的元素都是函式的形式等),那麼只有當這個被返回的函式被呼叫時才會返回值。
現在通過例子說明一下啊:

返回函式,只有被返回的函式被呼叫時,才會計算裡邊引數的值

def count():
    fs=[]
    for i in range(1,4):
        def f():
            return i*i
        fs.append(f)
    return fs      

def main():
    f1,f2,f3=count()    #因為count()返回的是一個list 裡邊的元素都是函式,f1,f2,f3就以此對應了list列表中的三個函式,
    result1=f1()   #只不過這三個函式只有等到自己被呼叫時f1()時才會返回原本儲存在函式中引數的值
    result2=f2()
    result3=f3()  #由於f()函式在定義時使用的時變化的i,因此前兩次迴圈 fs列表中對應的元素--函式儲存的並不是原來的1 和2 而是最後被更新到3
    print(result1,result2,result3)

main()

需要說明一下,上面的程式碼執行之後返回的都是9 。
首先呼叫count()函式,返回的是fs,一個list 只不過這個list函式存放的元素不是普通的整數或者字元,在這裡是函式f,主函式裡f1,f2,f3=count() 後就會把三次迴圈結束的函式依次賦值給 f1,f2,f3,也就是現在他們三個仍是函式,並不是某個具體的值,而且這三個函式裡儲存著需要返回的引數i ,只有當f1() f2() f3()這樣被呼叫時,才會依次返回值。
而且返回的值都是9並不是期望的1,4,9 原因是前一次迴圈的i 被後一次給覆蓋了,所以最後返回的都是i=3 即3*3=9的值

那如果想要返回1,4,9呢,下面提供兩種方法:

#當然如果你想輸出1 4 9的話 一種方法是返回的時候直接就呼叫這個函式,當然這種方法返回的就不是具體的函數了

def count():
    fs=[]
    for i in range(1,4):
        def f():
            return i*i
        fs.append(f())  #原本使用的是fs.append(f) 原來列表中存的是函式,現在列表中存放的是具體的值,因為f()就會呼叫上買你的函式,直接就會計算出來
    return fs

def main():
    r1,r2,r3=count()
    print(r1,r2,r3)

main()

另外一種仍然採用返滬函式的程式碼實現如下:

#如果還想返回的是一個函式,而不是具體的值,然後向輸出1,4,9,怎麼辦呢,只需要在返回函式的地方,讓他不再使用變化的引數即可

def count():
    fs=[]

    def f(i):  #定義一個函式,返回值仍然是一個函式
        def g():
            return i*i
        return g

    for i in range(1,4):
        fs.append(f(i))   #這裡呼叫函式之後會儲存這個引數的值,而不是再下一次迴圈中把上一次的引數值給沖掉
    return fs

def main():
    f1,f2,f3=count()  #count()函式返回的仍然是列表fs,裡邊的值仍然是函式,只不過函式裡儲存的引數i依次是1,2,3即下一次迴圈的值不會吧上一次迴圈的值給沖掉
    print(f1(),f2(),f3())  #只有對fs列表中存放的三個函式都進行呼叫時才會返回值

main()

”利用閉包返回一個計數器函式,每次呼叫它返回遞增整數:“
我在編寫的時候把要呼叫的次數使用者輸入了,直接輸出每次呼叫的結果
程式碼如下:

#利用閉包返回一個計數器函式,每次呼叫它都會返回一個遞增的整數

def count():           #count()函式就是會返回一個計數器函式 increase()

    def increase(n):   #increase()函式裡面的引數是第幾次呼叫它 
        number=0
        for i in range(n):   #根據呼叫的次數返回number的值,相當於就是計數累加
            number+=1
        return number

    return increase

def main():
    n=eval(input('please input a number:'))    #輸入想呼叫計數器的次數n ,也就是說明想累加幾次
    for i in range(n):
        f=count()   #現在f就是count()函式的返回值 是一個函式 increase()
        result=f(i) #increase(i) 就會返回第i次呼叫increase()函式累加的值
        print(result,end=' ')

main()

上面的程式碼其實邏輯上不對,修改如下:

#利用閉包返回一個計數器函式,每次呼叫它都會返回一個遞增的整數
#20180206裡面寫的不對啊,邏輯不對
def count(n):   #count(n)函式就是會返回一個計數器函式increase(n),為什麼這裡要加一個引數,裡面又重新定義了一個新的函式g呢是為了儲存當前的變數值,防止下一次賦值把原來的變數值給沖掉

    def increase(i):    #這裡i其實就是第幾次呼叫的意思
        def g():
            sum=0
            for j in range(i):
                sum+=1
            return sum
        return g

    return increase(n)


def main():
    n=eval(input("please input a number:"))
    for i in range(n):
        f=count(i)
        result=f()
        print(result)

main()

哎,實現一個功能得想半天,,,難過

“請用匿名函式改造一下函式:”

#請用匿名函式改造下面的程式碼:
def is_odd(x):     #判斷是否為奇數
    return x%2==1

def main():
    print(list(filter(is_odd,range(1,20))))

main()

使用匿名函式改寫為:
list(filter(lambda x:x%2==1,range(1,20))) 即可。
匿名函式中:號前面的是函式的引數,匿名函式的返回值是後面的表示式的值

裝飾器

“請設計一個decorator,它可作用於任何函式上,並列印該函式的執行時間:”
這個程式碼是參考的這位大神

import time 
import functools
def log_time(func):
    @functools.wraps(func)
    def wrapper(*args,**kw):
        start=time.time()
        res=func(*args,**kw)
        end=time.time()
        print("%s runned in %s seconds"%(func.__name__,(end-start)*1000))
        return res
    return wrapper

@log_time
def f1(x,y):
    time.sleep(1)
    return x+y

def main():
    result=f1(1,2)
    print(result)

main()

下面的程式碼是我的一點點拓展:

#this code is to creat another feature

import functools,time

def log_time(text):  #給裝飾器log_time()傳入一個引數
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args,**kw):
            start=time.time()    
            fun=func(*args,**kw)
            end=time.time()
            print("the progame %s runned in %s ms"%(func.__name__,(end-start)*1000))
            return fun
        return wrapper
    return decorator

#log_time(text)(f1)    
#先log_time(text) 返回一個decorator 函式,然後再decorator(f1) 在返回一個wrapper函式  至此就是@log_time("xuanxuan,,,") 和接下倆def f1()
@log_time("xuanxuan,you are a beautiful girl!")   
def f1(x,y,z):
    time.sleep(2) #作用是讓函式暫停2s在執行,就是計算函式執行的時間的
    return x+y+z


def main():
    result=f1(2,3,4)
    print("the result and the name of this function is {} and {} ".format(result,f1.__name__))

main()

以下兩個小題的答案參考自這位大神
“請編寫一個decorator,能在函式呼叫的前後打印出’begin call’和’end call’的日誌。”

相對比較簡單,直接上程式碼:

#在函式執行的前後輸出begin call和end call:

import functools
def log(func):
    @functools.wraps(func)
    def wrapper(*args,**kw):
        print("begin call %s():"%func.__name__)
        res=func(*args,**kw)
        print("the result of the program is {}".format(res))
        print("end call %s():"%func.__name__)
        #resturn res
    return wrapper

@log
def f2(x,y):
    return x+y

def main():
    f2(4,6)
    print("the name of the function is :"+f2.__name__)

main()

“再思考一下能否寫出一個@log的decorator,使它既支援:
@log
def f():
pass
又支援:

@log(‘execute’)
def f():
pass”

import functools

def log(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args,**kw):
            if text!=None:
                print("the text is %s and the call %s(): "%(text,func.__name__))
                res=func(*args,**kw)
                return res
            else:
                print("call %s ():"%func.__name__)
                res=func(*args,**kw)
                return res
        return wrapper

    if isinstance(text,str):  #首先如果有引數 就跟原來一樣直接返回decorator即可
        return decorator
    else:    #如果沒有引數 其實log(func)就是log裡邊其實直接傳的引數就是func 返回的應該是wrapper  
        func=text
        text=None
        return decorator(func)  #所以這裡的應該是直接decorator(func) 返回wrapper

@log("there is a parameter in this edition")
def f1(x,y):
    return x*y

def main1():
    result=f1(2,3)
    print("the result is {}".format(result))
    print("the name of this function(no_parameter) is "+f1.__name__)

@log
def f2(x,y):
    return x+y

def main2():
    result=f2(5,8)
    print("the result of this function(with parameter) is {}".format(result))
    print("the name of this function is "+f2.__name__)

def main():
    number=eval(input("please input a number to decide which the function to run:"))
    if number==1:
        main1()
        print("run successfully!")
    else:
        main2()
        print("Run successfully!")

main()

偏函式

簡單的說 引入functools.partial 之後 就可以把原來函式的引數給固定住,從而建立一個新的函式,當然對於新的函式我們仍然是可以傳入新的引數值的。

import functools

def main1():
    int2=functools.partial(int,base=4)
    result=int2('1000000')
    print(result)

def main2():
    max2=functools.partial(max,100)
    result=max(1,2,4,7,99)
    result1=max(1,2,3,102)
    print((result,result1))

#main1()   
main2()