天天看點

python學習(3)函數式程式設計

是一種程式設計方法,或者說是程式設計模式,它将電腦運算視為函數的計算、不需要變量因而不會産生副作用、支援高階函數(可以接受函數作為參數)。

python并不是純的函數式程式設計,它允許有變量,支援閉包,有限的支援匿名函數。

與以前的c++不同的是python的變量是可以指向函數的,而函數名其實也是一個指向函數的變量。

可以看看下面的例子:

f = abs

f(-10)

當然我們可以想到指向函數的變量其實也可以作為函數參數,這就相當于函數作為參數,就是我們說的高階函數。

看下面的例子:

def add(x, y, f):

return f(x) + f(y)

 add(-5, 6, abs)

接受一個函數f和一個list,list裡的元素依次通過f的處理,然後生成一個新的list。

看這個例子:

def format_name(s):

    return s[0].upper()+s[1:].lower()

print map(format_name, ['sam', 'lily', 'tom'])

輸出:['sam', 'lily', 'tom']

接受一個函數f和一個list,f必須接受兩個參數,取list中前兩個元素用f處理,結果再作為參數和後面的元素一起用f處理,依次類推。

from functools import reduce

def add(x, y):

     return x + y

reduce(add, [1, 3, 5, 7, 9])

輸出:25

用于過濾序列,接受一個函數f和一個list,函數f對list中每個元素進行判斷,傳回true或false,過濾掉不符合條件的元素,生成由符合條件的元素組成的序列。

例如下面這個過濾偶數的例子:

def is_odd(x):

return x % 2 == 1

filter(is_odd, [1, 4, 6, 7, 9, 12, 17])

用于對list進行排序,第二個參數可以省略也可以傳入一個函數cmp,來定義排序的規則:順序的傳回-1,逆序傳回1,相等傳回0。第三個參數如果設為reverse=true可以在原來的基礎上逆序排序。

def cmp_ignore_case(s1, s2):

    if s1.upper()<s2.upper():

        return -1

    else:

        return 1

print sorted(['bob', 'about', 'zoo', 'credit'], key=cmp_ignore_case, reverse=true)

結果:['zoo', 'credit', 'bob', 'about']

python可以傳回函數,但有一點需要注意的:

def calc_prod(lst):

    def prod(x1, x2):

        return x1 * x2

    def c_prod():

        return reduce(prod, lst)

    return c_prod

f = calc_prod([1, 2, 3, 4])

print f()

輸出:24

這裡傳回函數的時候并不會執行,再調用f()的時候才會執行。

先簡單的下個定義:内層函數引用了外層函數的變量(參數也算變量),然後傳回内層函數的情況,稱為閉包(closure)。

但是為什麼要這樣子做呢?我們結合下面這個例子來了解一下:

def line_conf(a, b):

    def line(x):

        return ax + b

    return line

line1 = line_conf(1, 1)

line2 = line_conf(4, 5)

print(line1(5), line2(5))

顯然如果我們隻是定義一個接受a、b、x三個參數的函數也是可以的,但這樣我們每次就需要傳入三個參數,顯然麻煩很多,特别是當a、b兩個參數較少改變的時候,上面代碼使用閉包的簡便性就展現出來了。這就是說閉包能提高程式的複用性。

一個需要注意的點:傳回函數不要引用任何循環變量,或者後續會發生變化的變量。這個可以看看廖雪峰老師慕課網上的python進階教程深入了解。

python匿名函數的使用比java的簡單得多:用關鍵字lambda且隻能有一個表達式。

print filter(lambda s:s and len(s.strip()) > 0, ['test', none, '', 'str', '  ', 'end'])

這個例子的功能是過濾空字元串,冒号前面的x是參數,後面是表達式。

裝飾器(decorator)用于增強函數的功能。本質上就是一個高階函數,它接收一個函數作為參數,然後,傳回一個新函數。

我們可以用@語句簡化像f = decorate(f)這樣的代碼。

def log(f):

    def fn(x):

        print 'call ' + f.__name__ + '()...'

        return f(x)

return fn

@log

def factorial(n):

    return reduce(lambda x,y: x*y, range(1, n+1))

print factorial(10)

輸出:

call factorial()...

3628800

但是,我們發現log()函數隻适用于一個參數的函數。為了讓它适用各種參數數量,我們需要做一些修改:

    def fn(*args, **kw):

        return f(*args, **kw)

然後有時,我們需要log()函數列印不同的語句,這就需要我們對裝飾器傳入參數。

思路是這個樣子的:帶參數的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()

[debug] test()...

none

裝飾器有時會改變函數的一些屬性,比如函數名等,為了儲存原有的函數資訊,我們可以在參數為原函數的函數下加一句 @functools.wraps(f)。

我們可以用 functools.partial() 減少原函數參數的數量,建立一個新函數。

int2 = functools.partial(int, base=2)

int函數預設第二個參數為10,表示進制,我們也可以改變第二個參數,例子的int2表示一個二進制的類型轉換函數。