
在本文中,您将了解什麼是函數範型,以及如何在Python中使用函數式程式設計。
在Python中,函數式程式設計中的map和filter可以做與清單相同的事情。這打破了Python的禅宗規則之一,是以函數式程式設計的這些部分不被認為是“Python式的”。
但是由于函數式程式設計高階程式設計的必經之路,是以我們需要了解甚至熟練掌握。
指令範式和函數範式
我們先對比一下程式設計中的指令範式兩個概念:
在指令式範式中,您通過給計算機一個任務序列來完成任務,然後它執行這些任務。在執行它們時,它可以改變狀态。
例如,假設你一開始把A設為5,然後你改變A的值,你有變量,在這個意義上,變量内部的值是變化的。
在函數範型中,你不告訴計算機要做什麼,而是告訴它是什麼。例如:一個數的最大公約數是多少,從1到n的乘積是多少,等等。
是以,變量不能改變。一旦你設定了一個變量,它就會一直保持這種狀态(注意,在純函數語言中它們不被稱為變量)。
所謂"副作用"(side effect),指的是函數内部與外部互動(最典型的情況,就是修改全局變量的值),産生運算以外的其他結果。
函數式程式設計強調沒有"副作用",意味着函數要保持獨立,所有功能就是傳回一個新的值,沒有其他行為,尤其是不得修改外部變量的值。
讓我們來看一個典型Python代碼的例子:
a = 3def some_func(): global a a = 5some_func()print(a)
這段代碼的輸出是5。在函數範型中,改變變量是一個大禁忌,而讓函數影響它們範圍之外的東西也是一個大禁忌。函數唯一能做的就是計算并傳回結果。
現在你可能會想:“沒有變量,就沒有副作用?”這有什麼好處呢?
如果一個函數使用相同的參數被調用兩次,那麼它肯定會傳回相同的結果。因為函數沒有副作用,如果你正在建構一個計算的程式,你可以加速這個程式。
如果程式知道func(2)等于3,我們可以将其存儲在一個表中。這可以防止程式在我們已經知道答案的情況下重複運作相同的函數。
Map
為了了解map,讓我們首先看看什麼是iterables。
iterable是任何可以疊代的東西。通常這些是清單或數組,但是Python有許多不同類型的疊代器。您甚至可以建立自己的對象,這些對象可以使用Python中魔法方法進行疊代。
這裡有兩個方法:
class Counter: def __init__(self, low, high): # set class attributes inside the magic method __init__ # for "inistalise" self.current = low self.high = high def __iter__(self): # first magic method to make this object iterable return self def __next__(self): # second magic method if self.current > self.high: raise StopIteration else: self.current += 1 return self.current - 1
“魔法方法是python内置方法,不需要主動調用,存在的目的是為了給python的解釋器進行調用,幾乎每個魔法方法都有一個對應的内置函數,或者運算符,當我們對這個對象使用這些函數或者運算符時就會調用類中的對應魔法方法,可以了解為重寫内置函數。”
第一個神奇的方法是用“__ iter__”傳回疊代對象,通常在循環開始時使用。
如果我們運作:
for c in Counter(3, 8): print(c)
那麼将會輸出:
345678
在Python中,疊代器是一個對象,它隻有一個簡單的魔法方法。這意味着您可以通路對象中的位置,但不能周遊對象。有些對象将使用方法__next__,如上面代碼中第二個例子。
現在我們知道了什麼是可疊代對象,讓我們回到map函數。map函數允許我們将一個函數應用到iterable中的每個項。通常,我們希望對清單中的每一項都應用一個函數,但是要知道對于大多數疊代器來說都是可能的。
Map接受兩個輸入,即要應用的函數和可疊代的對象:
map(function, iterable)
假設我們有一個清單:
[1, 2, 3, 4, 5]
我們希望将清單中的每一個數字進行平方,那麼可以這麼寫代碼:
x = [1, 2, 3, 4, 5]def square(num): return num*numprint(list(map(square, x)))
Python中的函數是惰性的。如果我們代碼中不包含“list()”,函數将存儲疊代的定義,而不是一個清單。我們需要顯式地告訴Python“将這個轉換為一個清單”,以便我們使用它。
現在寫一個像“square(num)”這樣的普通函數很好,但是它看起來不太對。我們必須定義一個完整的函數才能在map中使用一次?我們可以使用lambda(匿名)函數在map中定義一個函數。
lambda表達式
lambda表達式是一個單行函數。舉個例子,這個lambda表達式對給定的一個數字求平方:
square = lambda x: x * x
運作程式:
>>> square(3)9
告訴Python這是一個lambda函數,輸入被稱為x,冒号後面的内容就是你對輸入的操作,它會自動傳回結果。
現在我們可以将上面的程式簡化:
x = [1, 2, 3, 4, 5]print(list(map(lambda num: num * num, x)))
Reduce
Reduce是一個函數,它把一個可疊代的東西變成一個東西。通常,您在一個清單上執行計算以将其縮減為一個數字。
Reduce是這樣的:
reduce(function, list)
我們可以(通常也會)使用lambda表達式作為函數。
清單的乘積是每一個單獨的數字相乘。要做到這一點,你可以:
product = 1x = [1, 2, 3, 4]for num in x: product = product * num
但是使用reduce你可以這樣寫:
from functools import reduceproduct = reduce((lambda x, y: x * y),[1, 2, 3, 4])
Filter
filter函數接受一個iterable并過濾掉在該iterable中不需要的所有東西。
filter通常接受一個函數和一個清單。它将函數應用于清單中的每一項,如果該函數傳回True,則不執行任何操作。如果傳回False,則從清單中删除該項目。
文法如下:
filter(function, list)
讓我們看看一個小例子,沒有過濾器,我們會寫:
x = range(-5, 5)new_list = []for num in x: if num new_list.append(num)
有了過濾器,這就變成:
x = range(-5, 5)all_less_than_zero = list(filter(lambda num: num < 0, x))
高階函數
高階函數可以将函數作為參數并傳回函數。一個非常簡單的例子如下:
def summation(nums): return sum(nums)def action(func, numbers): return func(numbers)print(action(summation, [1, 2, 3]))
partial application
部分應用程式(也稱為閉包)有點奇怪,但是非常酷。您可以調用一個函數而不提供它需要的所有參數。我們來看一個例子。
我們想要建立一個函數,它有兩個參數,一個底數和一個指數,并傳回底數的指數次方,就像這樣:
def power(base, exponent): return base ** exponent
現在我們想要一個專門的平方函數,用幂函數求出一個數的平方:
def square(base): return power(base, 2)
這是可行的,但如果我們想要一個立方體函數呢?或者是函數的4次方?我們能一直寫下去嗎?嗯,你可以。但是程式員很懶。
如果你一遍又一遍地重複同樣的事情,這是一個信号,表明有一種更快的方法可以加快速度,讓你不再重複。我們可以在這裡使用部分應用程式。
讓我們看一個例子的平方函數使用部分應用程式:
from functools import partialsquare = partial(power, exponent=2)print(square(2))
這是不是很酷?我們可以調用需要兩個參數的函數,隻需使用一個參數就可以告訴Python第二個參數是什麼。
- END -
原文連結:
https://medium.com/hackernoon/learn-functional-python-in-10-minutes-to-2d1651dece6f
文源網絡,僅供學習之用。如有侵權,聯系删除。
往期精彩
◆ 50款開源工具你都用過嗎?
◆ python+C、C++混合程式設計的應用
◆ python網絡爬蟲的基本原理詳解
◆ Python自動操控excel,一小時解決你一天的工作
◆ 如何用Python增強Excel,減少處理複雜資料的痛苦?