1. 程式人生 > >Day-6: 函數式編程

Day-6: 函數式編程

數字 功能 創建 結果 閉包 rip print 例如 cor

  函數式編程就是封裝成一個個函數,一次調用來完成復雜任務。

  函數式編程的一個特點是,允許把函數本身作為參數傳入另一個函數,還允許返回一個函數!

  • 高階函數

  高階函數就是將函數的變量名作為參數傳入,內部再對該函數進行調用的函數。

  一個簡單的高階函數如下:

def add(x, y, f):
    return f(x) + f(y)
x ==> -5
y ==> 6
f ==> abs
f(x) + f(y) ==> abs(-5) + abs(6) ==> 11
>>> add(-5, 6, abs)
11

  Python內建了map()、reduce()、filter()和sorted()等高階函數。

  map()函數接收兩個參數,一個是函數,一個是序列,map將傳入的函數依次作用到序列的每個元素,並把結果作為新的list返回。

>>> def f(x):
...     return x * x
...
>>> map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
[1, 4, 9, 16, 25, 36, 49, 64, 81]

  map()作為高階函數,事實上它把運算規則抽象了,因此,我們不但可以計算簡單的f(x)=x2,還可以計算任意復雜的函數,比如,把這個list所有數字轉為字符串:

>>> map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])
[1, 2, 3, 4, 5, 6, 7, 8, 9]

  reduce把一個函數作用在一個序列[x1, x2, x3...]上,這個函數必須接收兩個參數,reduce把結果繼續和序列的下一個元素做累積計算,其效果就是:

>>> def fn(x, y):
...     return x * 10 + y
...
>>> reduce(fn, [1, 3, 5, 7, 9])
13579

  filter()函數是用於過濾序列的,傳入一個函數和一個序列。filter()

把傳入的函數依次作用於每個元素,然後根據返回值是True還是False決定保留還是丟棄該元素,如:

def not_empty(s):
    return s and s.strip()

filter(not_empty, [A, ‘‘, B, None, C,   ])
# 結果: [‘A‘, ‘B‘, ‘C‘]

  sorted()函數是用於對序列進行排序,傳入一個序列和一個默認函數為cmp的函數。只傳入序列時,進行默認排序,如下:

>>> sorted([36, 5, 12, 9, 21])
[5, 9, 12, 21, 36]

  傳入序列和函數時,依據傳入的函數進行排序,如下:

def reversed_cmp(x, y):
    if x > y:
        return -1
    if x < y:
        return 1
    return 0
>>> sorted([36, 5, 12, 9, 21], reversed_cmp)
[36, 21, 12, 9, 5]
  • 返回函數

  一個函數的返回值不是變量,而是函數。這種方式可以構成“閉包”,對程序有極大的應用,如裝飾器。

  但是需要註意的是:返回函數不要引用任何循環變量,或者後續會發生變化的變量。

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

f1, f2, f3 = count()
>>> f1()
9
>>> f2()
9
>>> f3()
9

  如果一定要引用循環變量的話,方法是再創建一個函數,用該函數的參數綁定循環變量當前的值,無論該循環變量後續如何改變,已綁定到函數參數的值不變。

>>> def count():
...     fs = []
...     for i in range(1, 4):
...         def f(j):
...             def g():
...                 return j*j
...             return g
...         fs.append(f(i))
...     return fs
... 
>>> f1, f2, f3 = count()
>>> f1()
1
>>> f2()
4
>>> f3()
9
  • 匿名函數

  當Python中,傳入函數不需要顯式地定義時,就可以利用匿名函數直接帶入;同時,由於匿名函數沒有名字,不會出現函數名字沖突的情況。

  匿名函數的格式是:lambda x: x * x

  • 裝飾器

  當希望在已有的函數基礎上增加一部分功能,但是又不想重新改函數時,就可以使用裝飾器,進行動態的修改,例如:對一個函數增加日誌打印的功能。

def log(func):
    def wrapper(*args, **kw):
        print call %s(): % func.__name__
        return func(*args, **kw)
    return wrapper
@log
def now():
    print 2013-12-25
>>> now()
call now():
2013-12-25

另外,要改變打印內容時,用到3層套用,就是下面的情況:

import functools

def log(text):
    def decorator(func):
        @functools.wraps(func) # 裝飾後不改變原函數的內置屬性
        def wrapper(*args, **kw):
            print ‘%s %s():‘ % (text, func.__name__)
            return func(*args, **kw)
        return wrapper
    return decorator
@log(execute)
def now():
    print 2013-12-25
>>> now()
execute now():
2013-12-25
  • 偏函數

  對於已有的函數,如果有默認的參數值,但是我們最近常調用的是另一個參數值時,可以使用偏函數,生成默認值為另一個參數的新函數。

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

  相當於:

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

Day-6: 函數式編程