記錄一下Python函數式編程,高級的幾個BIF,高級官方庫方面的用法和心得。
函數式編程
函數式編程是使用一系列函數去解決問題,按照一般編程思維,面對問題時我們的思考方式是“怎么干”,而函數函數式編程的思考方式是我要“干什么”。很多好用的函數很大程度節約了編程成本。
函數參數問題
總結來說就三種基本的情況:
fun(a,b)
fun(a,*b)
:b是可迭代對象fun(a,**b)
:b是帶有檢索的迭代對象,在函數體內部解析的時候類似字典
其他的情況基本是上面的改動,注意fun(*a,**b)
這種形式是任意參數。
ls = [i for i in range(10)]
def fun1(a,*b):
for i in b:
print(a,i)
fun1(1,*ls)
def fun2(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)
fun2('AsuraDong',12,參數="random")
匿名函數:lambda
- 沒有return返回值,返回值就是表達式的值
- 函數沒有名字,不必擔心函數名沖突
- 匿名函數也是一個函數對象,也可以把匿名函數賦值給一個變量,再利用變量來調用該函數
fun = lambda x:x+1
print(fun(5)) #6
fun2 = lambda x,y:x*y
print(fun2(5,2)) #10
裝飾器:@
這種在代碼運行期間動態增加功能的方式,稱之為“裝飾器”(Decorator)。
沒有參數的裝飾器
# 定義一個裝飾器
def log(func):
def wrapper(*args, **kw):
print('call %s()' % func.__name__)
return func(*args, **kw)
return wrapper
@log
def now():
print('2015-3-25')
now()
注意裝飾器的內部邏輯關系(調用順序):log()->return wrapper -> wrapper() -> return func() -> now()
含參數的裝飾器
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
@log("可愛的參數")
def now():
print('2015-3-25')
進一步完善:保留函數的內置方法__name__不改變
如果調用now.__name__
,得到的結果是wrapper
而不是我們希望的now
(本來函數的名字)。顯然這與我們的初衷相背離。這是需要在wrapper
前面加上functools庫里的@functools.wraps(func)
。
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("可愛的參數")
def now():
print('2015-3-25')
print(now.__name__)
裝飾器用途
除了之前講的可以讓代碼更容易理解之外(但是確實不好寫),還有什么作用?本來我也覺得沒啥用。。。直到后來接觸NLTK,里面有一個@memorize
裝飾器,在遞歸的時候可以自動保留每次的結果,避免了手動去完善代碼(可以去翻之前的博客)。所以用處還是很大的。
歡迎進一步交流本博文相關內容:
博客園地址 : http://www.cnblogs.com/AsuraDong/
CSDN地址 : http://blog.csdn.net/asuradong
也可以致信進行交流 : xiaochiyijiu@163.com
歡迎轉載 , 但請指明出處 : )
BIF:內建的函數(built-in functions)
zip:將兩個迭代對象合成一個迭代對象
注意:多余的沒有匹配的迭代元素會被自動舍棄
a = [1,2,3]
b = 'avsss'
for i in zip(a,b):
print(i)
for i,j in zip(a,b):
print('Index:',i,"; Item:",j)
enumerate:返回的是迭代對象,由位置+元素構成
for i,j in enumerate(b):
print("Index:",i,":Item:",j)
filter:過濾函數
兩個參數,第一個是函數,第二個是一個可迭代對象。返回的值是一個可迭代對象,其中的每個元素是參數中迭代對象的每個元素在參數中的函數返回值為True的元素。(有點亂,看代碼)
list(filter(lambda m:m%2==0,range(1,6)))
結果是[2,4]
map:映射函數
用法和filter類似,區別如下:
- 參數里面的函數作用是對迭代對象每個元素操作
- 返回的被操作過的迭代對象
list(map(lambda m:m**2,range(1,6)))
結果是[1, 4, 9, 16, 25]
reduce
- 在functools庫里
- func接收兩個參數,reduce把結果繼續和序列的下一個元素做累積計算
#一道經典的1+2+..+100的例題
from functools import reduce
add = lambda x,y : x+y
ls = [i for i in range(1,101)]
print(reduce(add,ls))
sorted:排序函數
非常重要,主要是在對參數的調整上做工作,甚至可以實現對類對象的排序。
基本排序和倒序
默認的排序是從小到大,如果需要從大到小,那么應該修改reverse
參數為True
。
print(sorted([-1,0,-100],reverse = True))
print(sorted([-1,0,-100]))
key參數
key參數來指定一個函數,此函數將在每個元素比較前被調用。
sorted([-1,0,-100],key = abs,reverse = True)#對絕對值的大小進行排序
student_tuples = [
('john', 'A', 15),
('jane', 'B', 12),
('dave', 'B', 10),
]
print(sorted(student_tuples, key=lambda student: student[2])) #按數字排序
按照這個思路,可以實現對類的排序。當然這是根據類中的某一類元素來進行的。
class Student:
def __init__(self, name, grade, age):
self.name = name
self.grade = grade
self.age = age
def __repr__(self):
return repr((self.name, self.grade, self.age))
student_objects = [
Student('john', 'A', 15),
Student('jane', 'B', 12),
Student('dave', 'B', 10),
]
sorted(student_objects, key=lambda student: student.age)
多級排序
如果你想根據類里面的多個元素或者迭代對象中的多個元素來排序,那么就需要operator
庫里面的兩個函數。應該注意的是它們的參數對應的是名字還是位置。並且排序為了避免二義性,都是先以第一個參數為基礎依次進行排序。
from operator import itemgetter,attrgetter
class Student:
def __init__(self, name, grade, age):
self.name = name
self.grade = grade
self.age = age
def __repr__(self):
return repr((self.name, self.grade, self.age))
student_objects = [
Student('john', 'A', 15),
Student('jane', 'B', 12),
Student('dave', 'B', 10),
]
student_tuples = [
('john', 'A', 15),
('jane', 'B', 12),
('dave', 'B', 10),
]
print(sorted(student_objects, key=attrgetter('name','age')))
print(sorted(student_tuples, key=itemgetter(1,2)))
高級官方庫
itertools
itertools模塊提供的全部是處理迭代功能的函數,它們的返回值不是list,而是Iterator,只有用for循環迭代的時候才真正計算
itertools.count(start=0,step=1)
默認是從0開始,間隔為1。
import itertools
natuals = itertools.count(0)
for i in natuals:
print (i)
這段代碼會一直打印下去,直到遇到終止。
itertools.cycle(iterator)
將iterator中的元素無限循環下去。
cc = itertools.cycle('456')
for c in cc:
print(c)
itertools.repeat(obj[,最大重復次數])
將obj默認無限重復。
np = itertools.repeat('1A.', 3)
for i in np:
print(i)
itertools.chain(a,b,..,n,...)
將迭代器abc串聯起來,形成一個新的迭代器。
a = [1,2]
b =[3,4]
for i in itertools.chain(a,b):
print(i)
c = {"fef":1}
for i in itertools.chain(a,b,c):
print(i)
itertools.groupby(iterator)
將iterator中相鄰的重復元素挑出來。所以,如果想對一個迭代對象查找不重復的元素,可以縣排序,再調用這個方法。
for i in itertools.groupby('ABCA'):
print(i)
itertools.takewhile(func,iterator)
無限序列雖然可以無限迭代下去,但是通常我們會通過takewhile()等函數根據條件判斷來截取出一個有限的序列
natuals = itertools.count(1)
ns = itertools.takewhile(lambda x :x<=10,natuals)
print(list(ns))
組合生成器
迭代器 | 參數 | 結果 |
---|---|---|
product() | p, q, ... [repeat=1] | cartesian product, equivalent to a nested for-loop |
permutations() | p[, r] | r-length tuples, all possible orderings, no repeated elements |
combinations() | p, r | r-length tuples, in sorted order, no repeated elements |
combinations_with_replacement() | p, r | r-length tuples, in sorted order, with repeated elements |
product('ABCD', repeat=2) | AA AB AC AD BA BB BC BD CA CB CC CD DA DB DC DD | |
permutations('ABCD', 2) | AB AC AD BA BC BD CA CB CD DA DB DC | |
combinations('ABCD', 2) | AB AC AD BC BD CD | |
combinations_with_replacement('ABCD', 2) | AA AB AC AD BB BC BD CC CD DD |
collections
里面收集了很多常用的數據結構,例如計數器、隊列、順序字典等等。而這些很多繼承於基本的數據結構,所以可以調用對應的BIF。
Counter:計數器
用法如下:
from collections import *
c = Counter()
for ch in 'this is a string':
c[ch]+=1 #自動生成對應的鍵和值,值默認為0.每次出現則加1
print(c)
結果是:Counter({'i': 3, 's': 3, ' ': 3, 't': 2, 'h': 1, 'a': 1, 'r': 1, 'n': 1, 'g': 1})
deque:隊列
高效實現插入和刪除操作的雙向列表,適合用於隊列和棧。如果數據量大而插入刪除操作又多,可以使用deque。並且他繼承了list的方法。
多出的兩種常用方法:
- appendleft(obj):Add an element to the left side of the deque.
- popleft():Remove and return the rightmost element.
namedtuple
namedtuple是一個函數,它用來創建一個自定義的tuple對象,並且規定了tuple元素的個數,並可以用屬性而不是索引來引用tuple的某個元素。這樣一來,我們用namedtuple可以很方便地定義一種數據類型,它具備tuple的不變性,又可以根據屬性來引用,使用十分方便。
我們知道tuple可以表示不變集合,例如,一個點的二維坐標就可以表示成:p = (1,2)
。但是,看到(1, 2),很難看出這個tuple是用來表示一個坐標的。定義一個class又小題大做了,這時,namedtuple就派上了用場:
>>> from collections import namedtuple
>>> Point = namedtuple('Point', ['x', 'y'])
>>> p = Point(1, 2)
>>> p.x
1
>>> p.y
2
>>> isinstance(p, Point)
True
>>> isinstance(p, tuple)
True
defaultdict:key不存在時的dict
from collections import defaultdict
dd = defaultdict(lambda: 'N/A') #默認值是調用函數返回的,而函數在創建defaultdict對象時傳入
dd['key1'] = 'abc'
print(dd['key1'])
print(dd['不存在的'])
OrderedDict:留有順序的字典
順序是添加鍵值對的順序,這樣,在迭代的時候可以保持順序。並且可以實現先入先出等類似的字典對象。
from collections import OrderedDict
d = dict([('a', 1), ('b', 2), ('c', 3)])
print(d) # dict的Key是無序的
od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
print(od) # OrderedDict的Key是有序的