##一、函數式編程
函數式編程接近與數學計算,抽象程度很高,相應的和計算機底層結構差異越大,效率不如C語言等高。
函數式編程就是一種抽象程度很高的編程範式,純粹的函數式編程語言編寫的函數沒有變量,因此,任意一個函數,只要輸入是確定的,輸出就是確定的,這種純函數我們稱之爲沒有副作用。而允許使用變量的程序設計語言,由於函數內部的變量狀態不確定,同樣的輸入,可能得到不同的輸出,因此,這種函數是有副作用的。
Python對函數式編程提供部分支持。由於Python允許使用變量,因此,Python不是純函數式編程語言。
###1、高階函數
函數 == 對象
函數式編程中,函數本身像一個對象一樣,只是沒有內部數據狀態而,而函數名和變量名一樣,指向某個函數對象的地址。
a、如f = abs
此時f就代表abs函數,能計算絕對值了。而將abs看做一個變量,它也是可變的!! 注意賦值是不帶括號和參數的如果帶了,就是執行了。
b、如果執行abs = 10,ads將不再能計算絕對值了,它將變成10這個數字。abs函數丟失了!,此時想要重新可用,需要重啓Python交互式環境。
c、函數可以作爲參數,返回值,容器元素等,被傳遞,存儲等幾乎任何變量使用的地方。
1.1 map/reduce
map就是映射,就想數學中的映射函數一樣,是現實世界中一類十分常見的操作處理。map就是對一個序列中所有元素做某種變換,比如路徑字符串替換前面的盤符。使用方式如下,先定義一個處理的函數f,然後放到map(f, Iterable)中,最後返回一個lterator惰性計算的序列。
>>> def f(x):
... return x * x
...
>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]
reduce:
reduce 和map的不同點是函數參數個數必須爲2,即f(x1, x2),且操作序列的方式不同,它會將函數前一次計算的結果作爲參數x1傳入,然後取下一個數做x2傳入,所以reduce的Iterable的元素個數至少爲2,最後計算出一個結果。即
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
1.2 filter
如名,filter對列表中的元素進行過濾,傳入一個返回boolean的函數即可。
1.3 sort
如名,對列表中的元素進行排序,使用方式
sorted([36, 5, -12, 9, -21])
直接排序
sorted([36, 5, -12, 9, -21], key=abs)
添加排序函數,此排序函數返回一個值,然後根據返回值排序
sorted([36, 5, -12, 9, -21], key=abs, reverse=True)
反向
以上元素能方便的轉換成key的,如果有多級參數排序這種不好轉換成key的,在Python2裏面可以傳入cmp函數,但是在Python3裏面去掉了,搞了一個很複雜的操作。
2、返回函數
即在函數中創建另一個函數,然後作爲返回值返回,此函數並不會立即執行,需要在後面進行顯示的的調用之後纔會執行。此函數可以訪問外部函數的參數和局部變量,本質上是持有它們的引用, 可以訪問這個引用,以及改變它所指向的對象內部變量的值,但是不能改變這個引用所指向的地址及對象,只能外部函數改變,這和Java的函數裏面的匿名內部類相似,只是沒有想Java裏面一樣final化,Python裏面沒有這個機制,所有會有後面一個問題q:,比如
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum
f = lazy_sum(1, 3, 5, 7, 9)
print(f())
特點:
1、每次調用外部返回的函數都是不一樣的。
>>> f1 = lazy_sum(1, 3, 5, 7, 9)
>>> f2 = lazy_sum(1, 3, 5, 7, 9)
>>> f1==f2
False
問題q:返回的函數是持有內部局部變量的引用,並不是直接拷貝一個,所以如果在下次執行之前,局部變量發生改變,函數持有的數據也是改變了的。比如下面這種:
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
#因爲在循環中定義的函數,等到後面某個時間執行時,內部函數(閉包)指向的變量的值已經改變了,變成了3。所以輸出結果都是9
對於循環內的變量,如果一定要引用,方法是再創建一個函數,用該函數的參數綁定循環變量當前的值,無論該循環變量後續如何更改,已綁定到函數參數的值不變:
def count():
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
fs.append(f(i)) # f(i)立刻被執行,因此i的當前值被傳入f()
return fs
再看看結果:
>>> f1, f2, f3 = count()
>>> f1()
1
>>> f2()
4
>>> f3()
9
因爲傳參時會創建一個新的引用,count函數對i的改變,不可能再影響f的j參數了。缺點是代碼較長,可利用lambda函數縮短代碼。
2、lambda表達式——匿名函數
lambda表達式的格式:lambda 參數列表:一個表達式
參數列表可以有多種參數,當然,它還可以像一個普通的函數一樣,可以賦值傳遞等。
使用lambda可以簡化函數的編寫,更加快捷
lambda表達式可用來編寫跳轉表(jump table),就是行爲的列表或字典。例如:
L = [(lambda x: x**2),
(lambda x: x**3),
(lambda x: x**4)]
** 3、函數裝飾器 **
顧名思義,對函數加上一層包裝,增加其功能。
例如
def log(func):
@functools.wraps(func)
def wrapper(x):
print('start' + func.__name__)
ret = func(x)
print('end' + func.__name__)
return ret
return wrapper
@log
def abs(x):
if x > 0:
return x
else:
return -x
print(abs(-11))
Python裏面的函數名可以通過賦值改變其指向,所以其中的@log就相當於依據 abs = log(abs) 此時abs就相當於wapper函數,可在wrapper中接收相同的參數,返回函數值等。
使用裝飾器之後,原函數的函數名會改變,Python提供了一個funtools函數進行處理 如上面的@functools.wraps(func)就是把wrapper的函數名稱變回去,有些依賴簽名的工具需要這個。
如果decorator本身需要傳入參數,那就需要編寫一個返回decorator的高階函數,寫出來會更復雜。比如,要自定義log的文本:
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
這個3層嵌套的decorator用法如下:
@log('execute')
def now():
print('2015-3-25')
執行結果如下:
>>> now()
execute now():
2015-3-25
和兩層嵌套的decorator相比,3層嵌套的效果是這樣的:
>>> now = log('execute')(now)
我們來剖析上面的語句,首先執行log(‘execute’),返回的是decorator函數,再調用返回的函數,參數是now函數,返回值最終是wrapper函數。這樣就能往第三層的函數裏面傳入相同的參數,並且可以使用第一層的屬於Derector的參數了。巧妙的利用內部函數可以訪問外部函數參數和局部變量的特性。Python裏面函數基本也相當於對象了,外部函數的參數、局部變量被函數對象持有就不會消失。
4、functools提供的一些功能
functools.partial(f, a); 根據已有的函數f,自動傳入部分參數,然後以此創建一個新的函數。編碼中,通常需要創建某個函數的重載函數,原來函數所有參數都需要調用時傳遞,重載的只需傳遞部分的參數,另一部分在重載函數內傳遞默認值即可。functools.partial就是用來簡化這種操作的。
##Python的模塊、包
###1、定義
1、定義:
Python是將一個代碼文件稱作模塊,也即模塊就是一個.py文件。
包的定義和其它語言中一樣,但是Python的包裏面必須放上一個__init__.py文件,不管裏面有無內容,否則不會被當做包處理。
2、模塊文件頭:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
' a test module '
__author__ = 'Michael Liao'
第一、二行是標準註釋,第一行讓此文件可以直接在unix linux的機器上運行,第二行 表示使用標準的UTF-8編碼,
第四行當做模塊地註釋,任何寫在py文件開頭的字符串都當做文檔註釋。
第六行的表示作者
這些可以不寫,但一般寫了更好
###2、導入模塊
1、導入語句
import module_name #直接導入,系統的或者是同級目錄下的
from package_test import module_name #不是同級目錄下的
對導入模塊的搜索識別,默認情況下,Python解釋器會搜索當前目錄、所有已安裝的內置模塊和第三方模塊,搜索路徑存放在sys模塊的path變量中。
還可以手動指定要搜索的模塊:
第一種直接修改System.path,運行結束後失效
sys.path.append('/Users/michael/my_py_scripts')
第二種方法是設置環境變量PYTHONPATH,該環境變量的內容會被自動添加到模塊搜索路徑中。設置方式與設置Path環境變量類似。
2、作用域
def _private_function_name(xxx) 表示私有函數,a或者__a私有的變量,這樣前面加上兩條一條或兩條’'的表示私有的變量,這只是告訴調用者不要訪問這個函數或者變量,但是仍然是可以訪問的,因爲Python沒有機制保證這個變量或者方法不能被訪問。
###3、其他的:
__name__
在Python中表示特殊變量,可以直接被引用,但是有特殊用途,代碼中一般不創建這樣的變量。
比如__doc__
表示模塊文檔,__author__
表示作者,