python partition函数_如何使用正确的姿势进行高效Python函数式编程?
演講者:丁來強@Splunk ?PyConChina2015 北京站
9月12日與9月19日,PyConChina 2015上海站與北京站順利落下帷幕?!叭松喽?#xff0c;Python 當歌”是本屆的主題。 在PyCon北京場,Splunk的Tech Lead丁來強為大家帶來了兩場干貨滿滿的技術分享,收獲了業內無數好評。
關于函數式編程
有哪些函數式語言?
其實函數是語言很早就出現了,上世紀30年代出現的Lambda和50年代的LISP,比面向過程和對象的語言出現的更早,現代的Clojure,Erlang,Haskeel也為很多人所熟知,保持著很強的活力,Scala作為Spark和Kafka的實現語言也是相當的火。
什么是函數式語言?
和面向過程的編程語言(例如C等)和面向對象的語言(例如C++/Java等)相比,函數式語言是一種聲明式的編程規約范式。 簡單例子如下:
一個復雜些的例子:
計算如下字符串的值:expr = "28+32+++32++39” ==> 28+32+32+39 ==> 131
命令式的語言采用一個初始值,然后一直去是修改它,最終獲得結果。
而函數式風格通過函數的組合調用,通過函數的一層層轉換輸入輸出最終獲得結果。
作為一種風格,很多人的代碼里面可能已經有一些是函數式的了。
函數式編程的特點
函數式編程有如下特點:函數即為數據,第一等公民
高階函數
純函數: 避免狀態,無副作用
不可變數據結構
強編譯器
尾遞歸消除(TRE)
延遲,模式匹配(Pattern Match),Monads
這個議題除了Monads,其他都有所覆蓋。
回到Python,Python其實是一個具備了很強函數式能力的命令式編程語言,通過語言或者庫的支持,對以上幾乎所有特征都有所支持(除了強編譯器)。
一些函數語言編譯執行器可以在強預設下做很強的優化,例如直接并發,延遲處理或者次序調換等。 而Python卻沒有這一點支持,歸根結底是因為Python從一開始就是按照命令式語言進行設計的。 Guido大叔曾經說過這樣一段話:
"I have never considered Python to be heavily influenced by functional languages, ... I had made functions first-class objects, I didn't view Python as a functional programming language..."
盡管如此,函數式編程風格依然是一種非常不錯的風格。 主要有幾個原因:
更好的測試性(因為無狀態),也更可靠
更擅長流式與并發操作(例如Scala)
一些偏主觀的觀點: 例如函數式編程風格有的時候提供了一種更加簡潔巧妙的解決方案。 代碼更少,可讀性更好。
純函數第一等公民就像Guido所說,Python中的函數已經是第一等公民了。皆可以作為變量,也可以作為參數傳入傳出,也可以隨時Lambda定義,或者放入數據,所有操作符也都是已經函數化的了。
通過fn庫,函數定義的方式可以進一步簡化為Scala風格:
純函數
無副作用
無副作用體現在對輸入的數據本身無修改,對函數內部外部無狀態修改。
如下的例子都是一些反例。
修改了輸入
修改了外界狀態
修改了輸出,影響了原輸入
真正純的無狀態和副作用的函數應該如下:
但是這可能比較復雜,性能也不太好。 這就要引入函數編程里的可持久化數據結構。
可持久化數據結構一種支持修改,在不修改原版本的情況下,返回一個修改版本的數據結構。
Persistent Data
高階函數
高階函數就是接受或者返回函數的函數。
Python已有不錯的支持:map,filter,groupby,reduce
functools module
list comprehension
decorators
Mapmap是函數式編程語言中很重要的高階函數,接受函數對輸入進行轉換。
Filter
reduce接受函數對輸入進行過濾。
List Comprehension
Map/Filter在函數式編程中非常重要,然后Python里面list Comprehension可能適用的更加廣泛,過濾轉換,最終構造出list,set,dict等都非常簡單。
然而List Comprehension一些特性也需要注意,首先是第一層才是不可修改的,對于初學者而言,讀取方式也稍微奇怪(先for,再if,最后看開頭),另外內部存在for/if,并沒有函數模塊化。
GroupbyGroupby接受函數對數據進行分組:
ReduceReduce接受二元函數對數據進行聚集:
Reduce的實現可以理解為如下:
相對應的sum,mul也可以直接使用reduce來完成
Partial
首先一個簡單問題,如何構造一個默認是降序排列的Sorted2函數,如下:
一般的實現:
而使用Partial則簡單的多。
Partial還可以用來預先參數綁定。 例如:
ComposeCompose是常用來構建更高級函數的工具:
CurryingCurrying是對Partial的更進一步的擴展:
toolz.curried里面所有的函數都已經Curry化了。
Currying對于簡化參數化Decorator也是非常有用的。 例如:
遞歸相關技術
關于遞歸
一些函數式語言里面沒有loop,只能用遞歸。 而通常都支持尾遞歸消除(將遞歸轉化為內部loop)
用遞歸的理由
代碼邏輯更清晰。例如:
不用遞歸的原因三個原因使得遞歸沒有大量被使用,因為:遞歸調用有遞歸層數限制(Python是1000),超過會棧溢出。
重復計算。 fib(n-2)與fib(n-1)是存在重復計算的。
遞歸調用常常需要不同情況進行跳轉,需要大量使用overloading或者pattern match的技術。
關于尾遞歸消除(優化)尾遞歸優化可以消除遞歸層數的限制,要求遞歸只存在于函數調用的最后一行,并且沒有進一步計算。
如下是反例:
通常使用一個幫助函數,將計算放在計算放在參數傳遞時,是常用技巧:
Trampoline然而壞消息是: Python并不支持尾遞歸消除!(Guido: 怪我咯!)
但并不用擔心,Tranpline就是用來解決這個問題的。 添加fn.recur的decorator,對于要結束遞歸的分支,返回False開頭的tuple,否則返回True開頭的tuple即可。
消除重復計算Python自帶的lru_cache即可消除重復計算的問題:
另外推薦(cy)toolz里面的memoize,支持更多功能,例如cache可以讓代碼更簡潔。
支持重載Python語言本身是不支持函數重載的,但其語言自身函數功能也很強大:未命名參數,命名參數,變參,命名變參,解包機制等。
讓Python支持類似于C++/Java等里面的重載,只需要引入multipledispatch.dsipatch即可,需要注意一開始的初始化。
重載使得遞歸的邏輯更加簡潔
Haskell類強大的pattern match功能不僅支持類型重載,也支持參數特征匹配。 這在Python中通過庫也是支持的。 至于實現機制,有興趣的朋友可以看一下Python AST。
延遲遍歷器帶來的延遲計算是Python核心慣用法。 常見的例子有:xrange
tuple comprehension
itertools 模塊
dict.iter* 方法
generator
for-loop 協議
fn.Stream提供了進一步的語法糖,例如給跌代添加切片功能。
Generator對于實現無限迭代器是很方便的。
fn.Stream也支持通過流方式來實現。
更多迭代器可以在(cy)toolz.itertoolz中可以找到:統計: count,groupby,frequency
過濾: unique,partition
選擇: take,drop,first,last,n_th etc。
merge_sorted
并行
值得一提的是函數式編程天生就是支持并行的。
Map因為傳遞的函數是無狀態無副作用的,所以可以直接并發執行,加快執行效率。
Reduce同理,Reduce也是可以并發執行來進行二元聚集最終實現Log級別的性能優化。
Python多進程與分布式策略算法大師Knuth說過:"97%過早優化是罪惡之源",在選擇多進程或者分布式的時候考慮是否是唯一選擇。 可能的其他選項有:
選擇不同的Python解釋器: Cython,PyPy,numba等
某些情況下分布式增加的管理復雜度不如單點增加多核來的有效。
通過GPU提高計算效率是數據科學領域的一個趨勢。
IO密集型并一定普遍適用于增加多進程的情況。
Python并發選擇GIL的原因,計算密集型是的多線程沒有意義。
Python自帶multiprocessing庫提供了很不錯的高階接口。
分布式通用領域計算模型的選擇有Spark,Hadoop,Celery等對于數據科學方面,分布式numpy和全局數據矩陣發展的也非常快。
Python并發分布式庫可較為成熟,供選擇的也很多:自帶的Multiprocessing/RPC庫
IPython Cluster
scikit-learn 并行算法
Python Parallel(只有Py2),Celery
更多: joblib等
并發計算與數據分發并行計算只需要替換現有默認函數為并發函數即可。 例如Pool.map取代模塊的map。
然而并發與分布式計算需要考慮如何把數據傳入傳出模塊,一般的數據都是可以的。 然而Closure默認不能pickle化,這種情況下需要使用copy_reg擴展或者使用dill庫。
IPython Cluster因為使用dill庫,并不存在這個問題。
如下圖是自帶多進程庫,IPython Cluster與Celery的一個比較,其中橙色勾表示需要一些額外代碼來支持,叉表示需要較多額外工作支持。
總結
通過來強深入淺出的介紹,大家了解了如何使用Python進行高逼格函數式編程的技術,工具和實踐。 使用Python也可以享受函數編程所帶來的高模塊,可復用,并發流處理等方面的好處。
總結
以上是生活随笔為你收集整理的python partition函数_如何使用正确的姿势进行高效Python函数式编程?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: visual studio code p
- 下一篇: python如何打开txt文件、并算词频