【Python】【函數式編程】

來源:互聯網
上載者:User

標籤:pen   簡潔   編寫   cti   log   部分   計算   分支   ram   

#  【【函數式編程】】
"""
函數是Python內建支援的一種封裝,我們通過把大段代碼拆成函數,通過一層一層的函數調用,就可以把複雜任務分解成簡單的任務,這種分解可以稱之為面向過程的程式設計。函數就是面向過程的程式設計的基本單元。

而函數式編程(請注意多了一個“式”字)——Functional Programming,雖然也可以歸結到面向過程的程式設計,但其思想更接近數學計算。

我們首先要搞明白電腦(Computer)和計算(Compute)的概念。

在電腦的層次上,CPU執行的是加減乘除的指令代碼,以及各種條件判斷和跳轉指令,所以,組合語言是最貼近電腦的語言。

而計算則指數學意義上的計算,越是抽象的計算,離電腦硬體越遠。

對應到程式設計語言,就是越低級的語言,越貼近電腦,抽象程度低,執行效率高,比如C語言;越進階的語言,越貼近計算,抽象程度高,執行效率低,比如Lisp語言。

函數式編程就是一種抽象程度很高的編程範式,純粹的函數式程式設計語言編寫的函數沒有變數,因此,任意一個函數,只要輸入是確定的,輸出就是確定的,這種純函數我們稱之為沒有副作用。而允許使用變數的程式設計語言,由於函數內部的變數狀態不確定,同樣的輸入,可能得到不同的輸出,因此,這種函數是有副作用的。

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

Python對函數式編程提供部分支援。由於Python允許使用變數,因此,Python不是純函數式程式設計語言。
"""
#【高階函數】

#變數可以指向函數
print (abs(-10))
print(abs) #<built-in function abs>


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

# 【高階函數】【map】
def f(x):
return x * x
r = map(f,[1,2,3,4,5])
print(list(r)) #[1, 4, 9, 16, 25]
print(r) #<map object at 0x1022454a8>
#一行代碼就是
print(list(map(f,[1,2,3,4])))#[1, 4, 9, 16, 25]

#【高階函數】【reduce】
from functools import reduce
def add(x,y):
return x + y
r = reduce(add,[1,2,3,4])
print(r) #10
#當然,求和運算也可以用python內建函數sum,沒必要動用reduce.
#但是,如果要把序列[1,3,5,7,9]變成整數13579,reduce就可以派上用場
from functools import reduce
def fn(x,y):
return 10 * x + y
print(reduce(fn,[1,3,5,7,9])) #13579

# str 轉 int
from functools import reduce

DIGITS = {"0":0,"1":1,"2":2,"3":3,"4":4,"5":5,"6":6,"7":7,"8":8,"9":9,"0":0}

def str2int(s):
def fn(x,y):
return 10 * x + y
def char2num(ss):
return DIGITS[ss]
return reduce(fn,map(char2num,s))
print(str2int("13579")) #13579

# 還可以用lambda函數進一步簡化
def str2int(s):
return reduce(lambda x,y:10 * x + y,map(lambda a: DIGITS[a],s))
print(str2int("2468")) #2468

# 練習1:利用map函數,把使用者輸入的不規範的英文名字,變為首字母大寫,其他小寫規範名字。
def nomalize(name):
return name.capitalize()
L1 = ["adam","LISA","barT"]
L2 = list(map(nomalize,L1))
print(L2) #[‘Adam‘, ‘Lisa‘, ‘Bart‘]

#練習2:python提供的sum()函數可接收一個list並求和。請編寫一個prod()函數,可接收一個list並通過reduce求積
def prod(L):
return reduce(lambda x,y:x * y,L)
print(‘1*3*5*4=‘,prod([1,3,5,4]))
if prod([1,3,5,4]) == 60:
print("測試成功!")
else:
print("測試失敗!")
‘‘‘
1*3*5*4= 60
測試成功!
‘‘‘
#練習3:利用map reduce編寫str2float,把字串"123.456"轉成浮點數123.456
#方法1 我自己寫的
from functools import reduce
def str2float1(s):
digits = {"1":1,"2":2,"3":3,"4":4,"5":5,"6":6,"7":7,"8":8,"9":9,"0":0}
def fn(x,y):
return 10 * x + y
def char2num(s):
return digits[s]
return reduce(fn,map(char2num,s.split(".")[0])) + reduce(fn,map(char2num,s.split(".")[1]))/10**len(s.split(".")[1])
print(‘str2float1(\‘123,456\‘)=‘,str2float1(‘123.456‘))
if str2float1(‘123.456‘) == 123.456:
print(‘測試成功!‘)
else:
print(‘測試失敗!‘)
‘‘‘
str2float1(‘123,456‘)= 123.456
測試成功!
‘‘‘

# 方法2:網上的,比較簡潔
from functools import reduce
def str2float2(s):
def fn(x,y):
return 10*x + y
n = s.index(".")
s1 = list(map(int,s[:n]))
s2 = list(map(int,s[n+1:]))
return reduce(fn,s1) + reduce(fn,s2)/10**len(s2)
print(‘str2float(\‘789.123\‘)=‘,str2float2(‘789.123‘))
if str2float2(‘789.1234‘) == 789.1234:
print(‘測試成功!‘)
else:
print(‘測試失敗!‘)
‘‘‘
str2float(‘789.123‘)= 789.123
測試成功!
‘‘‘

# 【高階函數】【filter】
# and & or
print(0 or 1) #1
print(1 or 0) #1
print(False or True) #True
print(True or 0) #True
print(False or 0) #0
print(‘***********‘)
print(0 and 1) #0
print(1 and 3) #3
print(1 and 0) #0
print(True and False) #False
print(False and 7) #False
print(1 and 7) #7
print(None and None.strip())#None

# 把一個序列中的Null 字元串刪掉
def not_empty(s):
return s and s.strip()
print(list(filter(not_empty,[‘A‘,‘‘,‘B‘,None,‘C‘,‘ ‘]))) #[‘A‘,‘B‘,‘C‘]

# 用filter求素數
‘‘‘
【分析】
計算素數的一個方法是埃氏篩法,它的演算法理解起來非常簡單:
首先,列出從2開始的所有自然數,構造一個序列:
2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...
取序列的第一個數2,它一定是素數,然後用2把序列的2的倍數篩掉:
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...
取新序列的第一個數3,它一定是素數,然後用3把序列的3的倍數篩掉:
5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...
取新序列的第一個數5,然後用5把序列的5的倍數篩掉:
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...
不斷篩下去,就可以得到所有的素數。
‘‘‘
# 分三步。1 先構造一個從3開始的奇數數列 2 定義一個篩選函數 3 定義一個產生器,不斷返回下一個素數
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)

# 由於primes()也是一個無限序列,所以調用時需設定一個退出迴圈的條件:
for n in primes():
if n < 10:
print(n)
else:
break
‘‘‘
2
3
5
7
‘‘‘

# 練習 回數指從左往右和從右往左都是一樣的數。利用filter()篩選出 回數
output = list(filter(lambda n: str(n)[0:] == str(n)[::-1],range(1,100)))
print(output)
if output == [1,2,3,4,5,6,7,8,9,11,22,33,44,55,66,77,88,99]:
print(‘測試成功!‘)
else:
print(‘測試失敗!‘)


# 【高階函數】【sorted】
print(sorted([36,5,-12,9,-21])) #[-21, -12, 5, 9, 36]
print(sorted([36,5,-12,9,-21],key=abs)) #[5, 9, -12, -21, 36]
print(sorted([‘bob‘,‘about‘,‘Zoo‘,‘Credit‘])) #[‘Credit‘, ‘Zoo‘, ‘about‘, ‘bob‘] 【備忘】字串排序,預設按照ASCII大小,由於大寫字母在小寫字母之前
#如果想忽略大小寫
print(sorted([‘bob‘,‘about‘,‘Zoo‘,‘Credit‘],key=str.lower)) #[‘about‘, ‘bob‘, ‘Credit‘, ‘Zoo‘]
#用大寫函數同樣適用
print(sorted([‘bob‘,‘about‘,‘Zoo‘,‘Credit‘],key=str.upper)) #[‘about‘, ‘bob‘, ‘Credit‘, ‘Zoo‘]
#反向排序
print(sorted([‘bob‘,‘about‘,‘Zoo‘,‘Credit‘],key=str.upper,reverse=True))#[‘Zoo‘, ‘Credit‘, ‘bob‘, ‘about‘]

# 練習:用一組tuple表示學生名字和成績
L = [(‘Bob‘,75),(‘Adam‘,92),(‘Bart‘,66),(‘Lisa‘,88)]
#請用sorted對上述列表分別按照名字和成績排序
def by_name(t):
return t[0].lower()
def by_score(t):
return t[1]
print(‘按照名字排序結果: ‘,sorted(L,key=by_name)) #按照名字排序結果: [(‘Adam‘, 92), (‘Bart‘, 66), (‘Bob‘, 75), (‘Lisa‘, 88)]
print(‘按照成績排序結果: ‘,sorted(L,key=by_score)) #按照成績排序結果: [(‘Bart‘, 66), (‘Bob‘, 75), (‘Lisa‘, 88), (‘Adam‘, 92)]

# 【【函數式編程】】【返回函數】
#函數作為傳回值
#實現一個可變參數的求和。通常情況下,求和函數這樣定義
def calc_sum(*args):
ax = 0
for n in args:
ax = ax + n
return ax
#但是,如果不需要立即求和,而是後面,根據需要再計算。可不直接返回求和結果,而是返回求和函數。
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) #<function lazy_sum.<locals>.sum at 0x101a5a048>
print(f()) #25
# 【注意】每次調用lazy_sum()時,都會返回一個新的函數,即使傳入相同的參數
f1 = lazy_sum(1,3,5)
f2 = lazy_sum(1,3,5)
print(f1 == f2) #False f1() f2()調用結果互不影響

# 閉包
def count():
fs = []
for i in range(1,4):
def f():
return i * i
fs.append(f)
return fs
f1,f2,f3 = count()
print(f1())
print(f2())
print(f3())
‘‘‘
9
9
9
全部都是9,原因在於,返回的函數引用了變數i,但它並非立即執行。等到三個函數都返回時,它們引用的變數已經編程了3,因此最終結果是9。
‘‘‘
#【注意】返回閉包牢記,返回函數不要引用任何迴圈變數,或者後續會發生變化的變數
#如果一定要引用迴圈變數,可再建立一個函數,用該函數的參數綁定迴圈變數當前的值,無論該迴圈變數後續如何修改,已綁定到函數參數的值不變。
def count2():
def f(j):
def g():
return j * j
return g
fs = []
for i in range(1,4):
fs.append(f(i))
return fs
f1,f2,f3 = count2()
print(f1())
print(f2())
print(f3())

‘‘‘
1
4
9
‘‘‘
# 方法內的變數,在方法幀未走完之前,一直有效 【備忘】我也不知道這麼形容對不對。。。只是我自己對此理解的一種方式。就像
import time
def now():
timeL = []
time1 = time.time()
time2 = time1 + 1000
time3 = time2 + 1000
timeL.append(time1)
timeL.append(time2)
timeL.append(time3)
def g(x):
return timeL[x]
return g
bb = now()
for i in range(0,3):
print(bb(i))
‘‘‘
1531637726.270685
1531638726.270685
1531639726.270685
‘‘‘

#練習:利用閉包返回一個函數,每次調用它都會返回遞增整數
#方法1
def createCounter():
a = [0]
def counter():
a[0] = a[0] + 1
return a[0]
return counter
createrA = createCounter()
print(createrA(),createrA(),createrA())
#方法2
a = 0
def createCounter2():
def counter():
global a
a = a + 1
return a
return counter
createrB = createCounter2()
print(createrB(),createrB(),createrB())


#【裝飾器】
def now():
print(‘2018-07-15‘)
f = now
print(now.__name__) #now
print(f.__name__) #now

# 兩層嵌套
‘‘‘
現在,假設要增強now()函數的功能,比如,在函數調用前後自動列印日誌,但又不希望修改now()函數的定義,這種在代碼運行期間動態增加功能
的方式,稱之為"裝飾器"(Decorator).
本質上,decorator就是一個返回函數的高階函數。
‘‘‘
def log(func):
def wrapper(*args,**kwargs):
print(‘call %s():‘%func.__name__)
return func(*args,**kwargs)
return wrapper
@log
def now():
print(‘2018-07-15‘)
now()
print(now.__name__)
‘‘‘
call now():
2018-07-15
wrapper
‘‘‘

‘‘‘
把@log放到now()函數的定義處,相當於執行now = log(now),由於log()是一個decorator,返回一個函數,原來的now()函數仍然存在,只是
現在同名的now變數指向了新的函數,於是調用now()將執行新的函數
‘‘‘
#decorator本身需要傳入參數。
def log(text):
def decorator(func):
def wrapper(*args,**kwargs):
print(‘%s %s():‘ % (text,func.__name__))
func(*args,**kwargs)
return wrapper
return decorator
@log(‘execute‘)
def now():
print(‘2018-07-15‘)
now()
#【解析】相當與定義now = log(‘execute‘)(now)

#經過decorator裝飾的函數,名字都會編程 wrapper ,為避免以來函數簽名的的代碼執行錯誤,不需要編寫wrapper.__name__ = func.__name__,python內建的functools.wraps就是做這個的。所以,一個完整的decorator如下。
import functools

def log(func):
@functools.wraps(func)
def wrapper(*args,**kwargs):
print(‘call %s():‘ % func.__name__)
return func(*args,**kwargs)
return wrapper

#或者帶參數的decorator
import functools
def log(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args,**kwargs):
print(‘%s %s():‘ % (text,func.__name__))
return func(*args,**kwargs)
return wrapper
return decorator()

#練習 設計一個decorator,可作用於任何函數上,並列印該函數的執行時間。
import time,functools
def printcalltime(func):
@functools.wraps(func)
def wrapper(*args,**kwargs):
start = time.time()
res = func(*args,**kwargs)
end = time.time()
print(‘%s execute time = %s ms‘ % (func.__name__,(end - start)*1000) )
return res
return wrapper

@printcalltime
def print1():
time.sleep(1)
print (1)

print1()
print(print1.__name__)
‘‘‘
1
print1 execute time = 1002.7890205383301 ms
print1
‘‘‘

# 練習
‘‘‘
寫出一個@log的decorator,使它既支援:

@log
def f():
pass

又支援:

@log(‘execute‘)
def f():
pass
‘‘‘
def log2(text):
if callable(text) == False:
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(‘execute %s for %s ‘ % (text,func.__name__))
return func(*args,**kwargs)
return wrapper
return decorator
else:
@functools.wraps(text)
def wrapper(*args,**kwargs):
return text(*args,**kwargs)
return wrapper
@log2
def now1():
print ("hello now1")

@log2(‘too late‘)
def now2():
print ("hello now2")

now1()
now2()
‘‘‘
hello now1
execute too late for now2
hello now2
‘‘‘

# 【偏函數】
#int函數,預設是10進位。
print(int(‘12345‘)) #12345
#int函數還提供base參數,預設是10,如果傳入,則按照參數的進位進行轉換
print(int(‘12345‘,base=8)) #5349
print(int(‘12345‘,8)) #5349
print(int(‘11‘,base=2)) #3
#假設要大量轉換二進位字串,每次都傳入int(x,base=2)太麻煩,於是想到定義一個int2()函數,預設把base=2傳進去
def int2(x,base=2):
return int(x,base)
#functools.partial就是建立偏函數的,不需要自己定義int2()
import functools
int2 = functools.partial(int,base=2)
print(int2(‘100‘)) #4
print(int2(‘101‘)) #5
‘‘‘
建立偏函數時,實際上可以接收函數對象、*args和**kw這3個參數,當傳入:

int2 = functools.partial(int, base=2)
實際上固定了int()函數的關鍵字參數base,也就是:

int2(‘10010‘)
相當於:

kw = { ‘base‘: 2 }
int(‘10010‘, **kw)
當傳入:

max2 = functools.partial(max, 10)
實際上會把10作為*args的一部分自動加到左邊,也就是:

max2(5, 6, 7)
相當於:

args = (10, 5, 6, 7)
max(*args)
結果為10。
‘‘‘























【Python】【函數式編程】

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.