歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Python基礎教程:函數式編程

函數式編程: 更抽象, 更脫離指令(計算機), 更貼近計算(數學).

  • 不需要變量 (python允許有變量, 所以python非純函數式)
  • 高階函數
  • 閉包: 返回函數
  • 匿名函數

高階函數

  • 變量可以指向函數 f=abs; f(-10)
  • 函數名: 就是指向函數的變量 abs=len
  • 高階函數: 接收函數作為參數的函數

    def add(x,y,f):
    return f(x)+f(y)
    add(-5, 9, abs)

map()

map()是 Python 內置的高階函數,它接收一個函數 f 和一個 list,並通過把函數 f 依次作用在 list 的每個元素上,得到一個新的 list 並返回。map()函數不改變原有的 list,而是返回一個新的 list。

def format_name(s):  
    return s.title()  
print map(format_name, ['adam', 'LISA', 'barT'])

reduce()

reduce()函數也是Python內置的一個高階函數。reduce()函數接收的參數和 map()類似,一個函數 f,一個list,但行為和 map()不同,reduce()傳入的函數 f 必須接收兩個參數,reduce()對list的每個元素反復調用函數f,並返回最終結果值。

reduce(function, iterable[, initializer])
Apply function of two arguments cumulatively to the items of iterable, from left to right, so as to reduce the iterable to a single value. If the optional initializer is present, it is placed before the items of the iterable in the calculation, and serves as a default when the iterable is empty. If initializer is not given and iterable contains only one item, the first item is returned.

filter()

filter()函數接收一個函數 f 和一個list,這個函數 f 的作用是對每個元素進行判斷,返回 True或 False,filter()根據判斷結果自動過濾掉不符合條件的元素,返回由符合條件元素組成的新list。

自定義sorted()

sorted()也是一個高階函數,它可以接收一個比較函數cmp來實現自定義排序,比較函數的定義是,傳入兩個待比較的元素 x, y,如果 x 應該排在 y 的前面,返回 -1,如果 x 應該排在 y 的後面,返回 1。如果 x 和 y 相等,返回 0。

返回函數

在函數內部定義一個函數 然後返回這個內部定義的函數.
返回函數可以把一些計算延遲執行

def calc_sum(lst):  
    def lazy_sum():  
        return sum(lst)  
    return lazy_sum

調用calc_sum()並沒有計算出結果,而是返回函數:

>>> f = calc_sum([1, 2, 3, 4])  
>>> f  
<function lazy_sum at 0x1037bfaa0>

對返回的函數進行調用時,才計算出結果:

>>> f()  
10

閉包

函數f內部定義的函數g無法被外部訪問 → 可以防止其他代碼調用g.

def calc_sum(lst):  
    def lazy_sum():  
        return sum(lst)  
    return lazy_sum

注意: 發現沒法把 lazy_sum 移到 calc_sum 的外部,因為它引用了 calc_sum 的參數 lst
像這種內層函數引用了外層函數的變量(參數也算變量),然後返回內層函數的情況,稱為閉包(Closure)。

閉包的特點是返回的函數還引用了外層函數的局部變量,所以,要正確使用閉包,就要確保引用的局部變量在函數返回後不能變
ex:

# 希望一次返回3個函數,分別計算1x1,2x2,3x3:  
def count():  
    fs = []  
    for i in range(1, 4):  
        def f():  
             return i*i  
        fs.append(f)  
    return fs  
f1, f2, f3 = count()

以為調用f1(),f2()和f3()結果應該是1,4,9,但實際結果全部都是 9 ! 原因就是當count()函數返回了3個函數時,這3個函數所引用的變量 i 的值已經變成了3。函數只在執行時才去獲取外層參數i, 由於f1、f2、f3並沒有被調用,所以,此時他們並未計算 i*i,當 f1 被調用時i已經變為3...
上面的正確寫法是:

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

因此,返回函數不要引用任何循環變量,或者後續會發生變化的變量。

匿名函數

Python中,對匿名函數提供了有限支持。
關鍵字lambda 表示匿名函數,冒號前面的 x 表示函數參數。匿名函數有個限制,就是只能有一個表達式,不寫return,返回值就是該表達式的結果。

map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])  
myabs = lambda x: -x if x < 0 else x   
>>> myabs(-1)  
1

裝飾器

問題: 定義了函數, 想在運行時增加函數功能同時不改動函數代碼.
ex. 希望函數調用時打印調用日志

⇒ 方法: 高階函數: 接收要修改的函數, 進行包裝後返回包裝過的新函數.

def new_f(f):  
    def fn(x):  
        print 'call %s()' % f.__name__  
        return f(x)  
    return fn

函數new_fn就是所謂裝飾器函數. python的@語法可以簡化裝飾器調用:

(注意: 右邊代碼, 原本未裝飾的f1函數已經被徹底隱藏了. )
優點: 極大簡化代碼.

無參數decorator

上面例子裡面的new_fn函數只能裝飾接收一個參數x的函數, 想要處理接收任意參數的函數 ⇒ 利用Python的 *args**kw
def log(f):
def fn(*args, kw):
print 'call %s() in %s'%( f.name, time.ctime() )
return f(*args,
kw)
return fn

帶參數decorator

接上面的log函數, 如果有的函數非常重要,希望打印出'[INFO] call xxx()...',有的函數不太重要,希望打印出'[DEBUG] call xxx()...',這時,log函數本身就需要傳入'INFO'或'DEBUG'這樣的參數,類似這樣:

@log('DEBUG')  
def my_func():  
    pass

把上面的定義翻譯成高階函數的調用,就是:
my_func = log('DEBUG')(my_func)
再展開一下:

log_decorator = log('DEBUG')  
my_func = log_decorator(my_func)

相當於:

log_decorator = log('DEBUG')  
@log_decorator  
def my_func():  
    pass

所以,帶參數的log函數首先返回一個decorator函數,再讓這個decorator函數接收my_func並返回新函數

def log(prefix):  
    def log_decorator(f):  
        def wrapper(*args, **kw):  
            print '[%s] %s()...' % (prefix, f.__name__)  
            return f(*args, **kw)  
        return wrapper  
    return log_decorator  
@log('DEBUG')  
def test():  
    pass  
print test()

這裡用到了閉包: 最裡層wrapper函數(即修飾過個函數)用到了prefix參數.

完善decorator

上面的decorator會修改函數名:

  • 在沒有decorator的情況下,打印函數名:
    def f1(x):  
        pass  
    print f1.__name__
    

⇒ 輸出: f1

  • 有decorator的情況下,再打印函數名:
    def log(f):  
        def wrapper(*args, **kw):  
            print 'call...'  
            return f(*args, **kw)  
        return wrapper  
    @log  
    def f2(x):  
        pass  
    print f2.__name__
    

⇒ 輸出: wrapper

這對於那些依賴函數名的代碼就會失效。decorator還改變了函數的__doc__等其它屬性。如果要讓調用者看不出一個函數經過了@decorator的“改造”,就需要把原函數的一些屬性復制到新函數中

def log(f):  
    def wrapper(*args, **kw):  
        print 'call...'  
        return f(*args, **kw)  
    wrapper.__name__ = f.__name__  
    wrapper.__doc__ = f.__doc__  
    return wrapper

這樣寫很不方便, Python內置的functools可以用來自動化完成這個“復制”的任務:

import functools  
def log(f):  
    @functools.wraps(f)  
    def wrapper(*args, **kw):  
        print 'call...'  
        return f(*args, **kw)  
    return wrapper

functools.wraps(f)是一個裝飾器函數, 目的是為了把最後返回的函數再次裝飾(復制f的屬性進去)... 所以對於帶參數的裝飾器, 應該在最裡面返回的wrapper函數前加上@functools.wraps(f)

import time, functools  
def performance(unit):  
    def perf_decorator(f):  
        @functools.wraps(f)  
        def wrapper(*args, **kw):  
            print 'call %s() in %s %s'%( f.__name__, time.time(), unit ) #closure  
            return f(*args, **kw)  
        return wrapper  
    return perf_decorator  
@performance('ms')  
def factorial(n):  
    return reduce(lambda x,y: x*y, range(1, n+1))  
print factorial.__name__

偏函數

假設要轉換大量的二進制字符串,每次都傳入int(x, base=2)非常麻煩,於是,我們想到,可以定義一個int2()的函數,默認把base=2傳進去:

def int2(x, base=2):  
    return int(x, base)

functools.partial可以把一個參數多的函數變成一個參數少的新函數,少的參數需要在創建時指定默認值,這樣,新函數調用的難度就降低了。

functools.partial(func[,*args][, **keywords])
Return a new partial object which when called will behave like func called with the positional arguments args and keyword arguments keywords.

import functools  
int2 = functools.partial(int, base=2)  
>>> int2('1000000')  
64  
>>> int2('1010101')  
85

sorted_ignore_case = functools.partial(sorted, key=lambda s:s.lower())  
print sorted_ignore_case(['bob', 'about', 'Zoo', 'Credit'])

CentOS上源碼安裝Python3.4  http://www.linuxidc.com/Linux/2015-01/111870.htm

《Python核心編程 第二版》.(Wesley J. Chun ).[高清PDF中文版] http://www.linuxidc.com/Linux/2013-06/85425.htm

《Python開發技術詳解》.( 周偉,宗傑).[高清PDF掃描版+隨書視頻+代碼] http://www.linuxidc.com/Linux/2013-11/92693.htm

Python腳本獲取Linux系統信息 http://www.linuxidc.com/Linux/2013-08/88531.htm

在Ubuntu下用Python搭建桌面算法交易研究環境 http://www.linuxidc.com/Linux/2013-11/92534.htm

Python 語言的發展簡史 http://www.linuxidc.com/Linux/2014-09/107206.htm

Python 的詳細介紹:請點這裡
Python 的下載地址:請點這裡

Copyright © Linux教程網 All Rights Reserved