1. 程式人生 > >【Python】程式設計筆記5

【Python】程式設計筆記5

文章目錄

函數語言程式設計(Functional Programming)

函數語言程式設計:思想更接近於數學計算,抽象程度很高的程式設計正規化,純粹的函數語言程式設計語言編寫的函式沒有變數。

特點:允許把函式本身作為引數傳入另一個函式,還允許返回一個函式。

Python 允許使用變數,因此,Python 不是純函數語言程式設計語言。

一、高階函式(Higher-order function)

函式本身也可以賦值給變數,即:變數可以指向函式。例如:函式名。

f = abs
print(f(-10))

輸出結果

10

高階函式:一個函式就可以接收另一個函式作為引數。(既然變數可以指向函式,函式的引數能接收變數)

def add(x, y, f):
    return f(x) + f(y)

print(add(-5, 6, abs))

輸出結果

11

二、map/reduce

Python 中內建了 map() 和 reduce() 函式。

1、map() 函式

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

def f(x):
    return x * x
r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
print(list(r))

## 把這個 list 所有數字轉為字串
print(list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])))

輸出結果

[1, 4, 9, 16, 25, 36, 49, 64, 81]
['1', '2', '3', '4', '5', '6', '7', '8', '9']

2、reduce() 函式

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

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

把序列[1, 3, 5, 7, 9]變換成整數 13579 的 reduce 實現:

from functools import reduce
def fn(x, y):
    return x * 10 + y
print(reduce(fn, [1, 3, 5, 7, 9]))

輸出結果

13579

str 型別 ==》int 型別

from functools import reduce
def str2int(s):
    def fn(x, y):
        return x * 10 + y
    def char2num(s):
        return {'0':0,'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9}[s]
    return reduce(fn,map(char2num,s))

# 13579
print(str2int('13579'))

利用 lambda 函式進一步簡化:

def char2num(s):
    return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
def str2int(s):
    return reduce(lambda x,y:10 * x + y, map(char2num, s))

str ==》float

def str2float(s):
    def char2num(s):
        return {'0':0,'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9}[s]
    def fnMuti(x, y):
        return 10 * x + y
    def fnDivid(x, y):
        return x / 10 + y
    dotIndex = s.index('.')
    return reduce(fnMuti, map(char2num, s[:dotIndex])) + reduce(fnDivid, list(map(char2num, s[dotIndex + 1:]))[::-1])/10
print('str2float(\'123.456\') =', str2float('123.456'))

輸出結果

str2float('123.456') = 123.456

三、filter

filter() 函式用於過濾序列。接收兩個引數:一個函式和一個序列,filter() 把傳入的函式依次作用於每個元素,然後根據返回值是 True ,則保留,為 False 則丟棄該元素。

def is_odd(n):
    return n % 2 == 1
print(list(filter(is_odd, [1,2,4,5,6,9,10,15])))
# 結果: [1, 5, 9, 15]

==》filter() 函式返回結果是 Iterator,是一個惰性序列,需要使用 list() 函式獲取所有的結果並返回 list。

1、用 filter 求素數

實現方法:埃氏篩法。列出從 2 開始的所有自然數,構造一個序列:取序列第一個數並篩掉其的倍數,依次類推…

# 生成器,且是一個無限序列
def _odd_iter():
    n = 1
    while True:
        n = n + 2
        yield n

# 過濾函式
def _not_divisible(n):
    return lambda x : x % n > 0

# 用於不斷返回素數的生成器
def primes():
    yield 2
    it = _odd_iter()    # 初始序列
    while True:
        n = next(it)
        yield n
        it = filter(_not_divisible(n), it)   # 構造新的序列

# 設定一個退出迴圈的條件
for n in primes():  
    if n < 1000:
        print(n)
    else:
        break

四、sorted

通常規定,對於兩個元素 x 和 y,如果認為 x < y,則返回-1,如果認為 x == y,則返回 0,如果認為 x > y,則返回 1

1、數字

## 原始
print(sorted([36, 5, -12, 9, -21]))
## sorted()函式也是一個高階函式,它還可以接收一個 key 函式來實現自定義的排序。
## key 指定的函式將作用於 list 的每一個元素上,並根據 key 函式返回的結果進行排序。
print(sorted([36, 5, -12, 9, -21], key=abs))

輸出結果

[-21, -12, 5, 9, 36]
[5, 9, -12, -21, 36]

2、字串

## 預設情況下,對字串排序,是按照 ASCII 的大小比較的
print(sorted(['bob', 'about', 'Zoo', 'Credit']))
## 可實現忽略大小寫的排序
print(sorted(['bob', 'about', 'Zoo', 'Credit'], key = str.lower))
## reverse 為 true 表示反向排序
print(sorted(['bob', 'about', 'Zoo', 'Credit'], key = str.lower, reverse = True))

輸出結果

['Credit', 'Zoo', 'about', 'bob']
['about', 'bob', 'Credit', 'Zoo']
['Zoo', 'Credit', 'bob', 'about']

五、返回函式

1、函式作為返回值

例如:返回求和的函式

def lazy_sum(*args):
    def sum():
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum
f1 = lazy_sum(1,3,5,7,9)
f1
f1()  # 函式需要呼叫才能執行
# 呼叫 lazy_sum()時,每次呼叫都會返回一個新的函式,即使傳入相同的引數
f2 = lazy_sum(1,3,5,7,9)
f1 == f2

輸出結果

<function __main__.sum>
25
False

分析:在函式 lazy_sum 中又定義了函式 sum,並且,內部函式 sum 可以引用外部函式 lazy_sum 的引數和區域性變數,當 lazy_sum 返回函式 sum 時,相關引數和變數都儲存在返回的函式中,這種稱為“閉包( Closure) ”的程式結構擁有極大的威力。

2、閉包

返回函式不要引用任何迴圈變數, 或者後續會發生變化的變數。

3、匿名函式

4、裝飾器

裝飾器”( Decorator):在程式碼執行期間動態增加功能的方式。

5、偏函式


模組

模組:Python中一個.py檔案就是一個模組(Module)
==》提高了程式碼的可維護性;複用性;避免函式名和變數名衝突。

包(Package):按目錄來組織模組的方法。例如:a.py(a模組) 與 b.py(b模組)與其他模組衝突,則選擇一個頂層包名,例如mycompany,則 a 模組變成 mycompany.a、b模組變成 mycompany.b
==》每一個包目錄下面都會有一個__init__.py 的檔案,這個檔案是必須存在的,用於標識包。

1、使用模組

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

'a test module'   ## 表示模組的文件註釋,任何模組程式碼的第一個字串都被視為模組的文件註釋

__author__ = '盛夏光年'

import sys

def test():
    args = sys.argv
    if len(args)==1:
        print("Hello world!")
    elif len(args) == 2:
        print('Hello, %s!' % args[1])
    else:
        print('Too many arguments!')

if __name__ == '__main__':
    test()

2、作用域

  • 正常的函式和變數名是公開的( public),可以被直接引用,比如: abc,x123, PI 等;
  • 類似__xxx__這樣的變數是特殊變數,可以被直接引用,但是有特殊用途;
  • 類似_xxx 和__xxx 這樣的函式或變數就是非公開的( private),不應該被直接引用,比如_abc, __abc 等;
    注意: Python 並沒有一種方法可以完全限制訪問 private 函式或變數

3、第三方庫

eg: Pillow、numpy、Jinja2
檢視搜尋目錄:

import sys
print(sys.path)

新增搜尋目錄——方法1:

import sys
sys.path.append('/Users/michael/my_py_scripts')

==》這種方法是在執行時修改,執行結束後失效。

新增搜尋目錄——方法2:
設定環境變數 PYTHONPATH