Python一等函數簡析

導語:本文章記錄了本人在學習Python基礎之函數篇的重點知識及我的心得,打算入門Python的朋友們能夠來一塊兒學習並交流。

本文重點:html

一、瞭解函數在Python中是一等對象;
二、瞭解Python中的可調用對象;
三、掌握正肯定義函數參數的方法;
四、瞭解operator和functools中支持函數式編程的方法。

1、函數是一等對象

一、一等對象

定義:一等對象是知足以下條件的程序實體:python

  • 在運行時建立;
  • 能賦值給變量或數據結構中的元素;
  • 能做爲參數傳給函數;
  • 能做爲函數的返回結果。

在Python中,全部函數都是一等對象。程序員

二、高階函數

定義:接受函數爲參數,或者把函數做爲結果返回的函數是高階函數。
在Python中傳統的高階函數有map,filter,reduce;經常使用的高階函數有內置函數sorted、min、max和functools.partial。正則表達式

  • map(function, iterable, ...):
    map返回一個迭代器,迭代器是經過function處理可迭代對象中的每一個元素產生的返回值的集合。
  • filter(function, iterable):
    filter至關於一個過濾器,以函數返回值爲斷定條件,篩選出True的元素並放入迭代器中返回。
  • functools.reduce(function, iterable[, initializer])
    reduce對可迭代對象中從左開始元素選出兩個進行函數運算,將返回的運算值做爲一個參數繼續與第三個元素進行函數運算,直至迭代完成返回運算值。

三、歸約函數:

定義:可以接受一個可迭代對象並返回單個結果的函數是歸約函數。
reduce就是歸約函數的一種,sum也是一種歸約函數。本章額外介紹兩個內置的歸約函數。express

all(iterable):
可迭代對象中每個元素都是真值則返回True,不然False。
any(iterable)
可迭代對象中存在一個元素是真值則返回True,不然False。
歸約函數會在第14章中討論可迭代對象時重點講解。編程

四、匿名函數:

匿名函數:使用lambda表達式建立的函數,函數自己沒有名字來辨識,於是叫作匿名函數。
句法特色:lambda函數只能使用純表達式,不能賦值,也不能使用while和try等語句。
語法:lambda [arg1 [,arg2,.....argn]]:expression。
優勢:建立方便,簡化代碼工做量。
缺點:代碼可讀性下降。數組

五、函數內省

在計算機編程中,自省是指這種能力:檢查某些事物以肯定它是什麼、它知道什麼以及它能作什麼。自省向程序員提供了極大的靈活性和控制力。性能優化

2、函數參數與註解

一、函數參數

在函數的定義過程可能須要傳入參數,對於函數涉及到的參數分爲如下四種:數據結構

  • 必填參數
    調用函數必須填寫的參數。在參數中居於靠前位置。
  • 默認參數
    當必填參數設置默認值時可選填。注意默認值是不可變對象,不然有邏輯錯誤。
  • 可變參數
    用單星號*args表示,即傳入的參數是不定的。*args把參數收到元組中接受。
    傳入方式既能夠是直接傳入,如func(1, 2, 3);也能夠用列表或元組傳入,如func(*(1,2,3))。
  • 強制關鍵字參數
    此類參數只能捕獲經過指定關鍵字傳入的參數,沒法按照位置順序讀參。定義時前面需放一個*。
  • 關鍵字參數
    此參數可填可不填。傳入方式分兩種,一是」傳遞參數名=傳遞參數值」形式的參數,這種方式傳入對位置無要求;二是不寫參數名,按照位置順序傳入參數值。
    當關鍵字參數不定時用雙星號**kw表示。**kw把關鍵字參數收到字典中接受。關鍵字參數既能夠直接傳入:func(a=1, b=2),又能夠先組裝dict,再經過**kw傳入:func(**{'a': 1, 'b': 2})。

參數組合:當函數涉及到使用多種參數時,定義和傳入的參數需按照的順序:
必填參數、默認參數、可變參數、強制關鍵字參數、關鍵字參數.併發

def func(name,country="China",*,age,**rest):
    print("name :",name," country :",country," age :",age," rest :",rest)
func("Jack","US",age=20)
func("Hoya",age=15,male="Boy",height="178",grade="excellent",country="UK")

#output:
name : Jack  country : US  age : 20  rest : {}
name : Hoya  country : UK  age : 15  rest : {'male': 'Boy', 'height': '178', 'grade': 'excellent'}
#強制關鍵字參數錯誤傳參
 func("Hoya",country="UK",20)

SyntaxError: positional argument follows keyword argument
#強制關鍵字參數只能利用關鍵字傳入參數

注:函數參數知識引自
做者:東皇Amrzs

二、獲取參數:inspect模塊

能夠經過A=inspect.signature(object)提取關於函數參數的信息;
signature支持signature.parameter方法返回關於參數的有序映射。
signature支持signature.bind(args, *kwargs)方法,此方法可將多個實參綁定到簽名的形參來接受。

三、函數註解:

註解(annotation)從Python3開始存在,用於爲函數聲明中的參數和返回值附加元數據。只有inspect.signature()能夠提取註解。
本人目前把註解簡單理解爲一種標籤。

3、可調用對象

定義:支持調用運算符()的對象叫作可調用對象。
判斷方法:利用內置的callable()函數判斷。

Python數據模型包含7種可調用對象:

  • 用戶定義的函數
    使用def語句或lambda表達式建立。
  • 內置函數
    使用CPython實現的函數,如len。
  • 內置方法
    使用CPython實現的方法,如list.pop。
  • 方法
    在類的定義體中定義的函數。
  • 類的實例
    若是類定義了__call__方法,這個類的實例能夠做爲函數調用。
  • 生成器函數
    使用yield關鍵字的函數或方法。調用生成器函數返回的對象是生成器。

下面針對類的實例爲示範進行調用操做:

class Text:
    def __init__(self,text):
        self.text=str(text)
    def number_search(self):
        import re
        num_search=re.compile(r"\d+")
        return print("number search :",num_search.findall(self.text))
    def __call__(self, *args, **kwargs):
        return self.number_search()
a=Text("asdljlj55fsa56af6af66f598as5asf6af59nf3asf830fa3s")
a.number_search()

#輸出:
number search : ['55', '56', '6', '66', '598', '5', '6', '59', '3', '830', '3']

從中能夠看出,建立函數類對象的簡便方式是實現__call__方法。

4、支持函數式編程的包

1. 函數式編程:

相比較命令式編程,函數式編程是經過函數來保存程序的狀態的。或者更準確一點,它是經過函數建立新的參數或返回值來保存程序的狀態的。

函數式編程與命令式編程對比

認識函數式編程應掌握的兩個本質:

  • 高階函數(higher-order functions)
    函數式編程是經過高階函數(higher-order functions)的特性來使其具備更豐富多變的表達能力。如map,filter。
    高階函數和一等函數讓基於函數演變的函數式語言表達能力大增,使其可以用函數構建起更高層更抽象的模塊來解決複雜的問題。
  • 沒有反作用(no side effect)
    函數全部功能就是返回一個新的值,沒有其餘行爲,尤爲是不得修改外部變量的值。
    使得函數式編程各個獨立的部分的執行順序能夠隨意打亂。 而這在命令式編程風格的代碼中是不可能的。
    執行順序的自由使其得以衍生出一大堆很是有用的特性,好比無鎖(lock-free)的併發操做、惰性求值(lazy evaluation),還有在編譯器級別上的各類性能優化技術。 特別在並行技術上,Clojure, Haskell, F#, Scala, Erlang這些函數式語言都無一例外地支持強大的併發功能。
    固然函數式語言不可能真的就不執行I/O,但它經過一些手段來把I/O的影響限制到最小,好比經過Continuations, Monad等技術。

注:函數式編程知識引自
做者:Jan Fan

Python的目標不是變成函數式編程語言,但經過operator和functools等包也能夠進行函數式編程,下面開始介紹這兩個模塊。

2. operator

本節介紹operator中的mul、itemgetter、attrgetter、methodcaller四種方法。

  • operator.mul(a,b)
    返回數字a和b的乘積。
import operator
from _functools import reduce
#計算階乘
def fact1():
    list1=filter(lambda x: x%2,range(8))
    return reduce(operator.mul,list1)
print(fact1())#輸出:105
  • operator.itemgetter(item or *items)
    建立一個接受集合的函數,返回指定索引對應的元素。若是指定索引至少爲2個,以元組形式返回查詢結果。
    After f = itemgetter(2), the call f(r) returns r[2].
    After g = itemgetter(2, 5, 3), the call g(r) returns (r[2], r[5], r[3]).
metro_data =[('Tokyo', 'JP', 36.933, (35.689722, 139.691667)),
('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)),
('Mexico City', 'MX', 20.142, (19.433333, -99.133333)),
('New York-Newark', 'US', 20.104, (40.808611, -74.020386)),
('Sao Paulo', 'BR', 19.649, (-23.547778, -46.635833))]
a=itemgetter(1,0)
for i in metro_data:
    print(a(i))#注意分清楚a和i誰是參數,被處理的可迭代對象是參數。

#輸出:
('JP', 'Tokyo')
('IN', 'Delhi NCR')
('MX', 'Mexico City')
('US', 'New York-Newark')
('BR', 'Sao Paulo')
  • operator.attrgetter(attr or *attrs)
    建立一個函數,根據名稱訪問對象的屬性,以元組的形式返回。
    After f = attrgetter('name'), the call f(b) returns b.name.
    After f = attrgetter('name', 'date'), the call f(b) returns (b.name, b.date).
    After f = attrgetter('name.first', 'name.last'), the call f(b) returns (b.name.first, b.name.last).

eg:按照城市經度順序輸出城市名和城市經度

from collections import namedtuple
from _operator import attrgetter

latlong=namedtuple("latlong","lat long")
citydata=namedtuple("citydata", "city ID pop coord")
city=[citydata(city,ID,pop,latlong(lat,long)) for city, ID,pop,(lat,long) in metro_data]
#拆包+列表推導。拆包注意city,ID,pop,latlong(lat,long)和citydata具名元組的結構對應關係,至於拆包用的變量名字是什麼並不重要,保證可讀性便可。
b=attrgetter("city","coord.lat")

#方法1
for i in sorted(city,key=attrgetter("coord.lat")):
    print(b(i))

#方法2
for i in sorted(city,key=lambda x: x[3][0]):
    print(b(i))

#輸出
('Sao Paulo', -23.547778)
('Mexico City', 19.433333)
('Delhi NCR', 28.613889)
('Tokyo', 35.689722)
('New York-Newark', 40.808611)
  • operator.methodcaller(name[, args...])
    自行建立函數,使用對象中支持的方法。args表明函數所需傳入的參數。
    After f = methodcaller('name'), the call f(b) returns b.name().
    After f = methodcaller('name', 'foo', bar=1), the call f(b) returns b.name('foo', bar=1).
from operator import methodcaller
c=methodcaller("upper")
d=methodcaller("islower")
print(c("apple"),d("apple"))
#輸出
APPLE True.

3. functools.partial

語法:functools.partial(func, *args, **keywords)
functools.partial適用於函數凍結參數的狀況。凍結參數是指咱們欲調用的函數中的部分或所有參數已經固定,只需補齊剩下的參數調用便可。能夠按照word編輯中的格式刷來理解。

from functools import partial
from unicodedata import normalize
clean=partial(normalize,"NFC")#字符串格式化,我以爲很像格式刷啊。
e="café"
f="cafe\u0301"
print(e==f) #False
print(clean(e)==clean(f))#True

使用技巧總結:operator中的itemgetter、attrgetter和functools.partial在使用上都須要先構建相似正則表達式的compile partern,即構建對應的itemgetter,attrgetter和partial,而後在partern基礎上傳入待處理對象。以剛纔的partial舉例就是clean(e),而不是e(clean)。

相關文章
相關標籤/搜索