Python實用教程_spiritx的博客-CSDN博客
高階函數(shù)是在Python中一個非常有用的功能函數(shù),所謂高階函數(shù)就是一個函數(shù)可以用來接收另一個函數(shù)作為參數(shù),這樣的函數(shù)叫做高階函數(shù)。高階函數(shù)是函數(shù)式編程的基本前提。
函數(shù)在 Python 是一等公民(First-Class Object),函數(shù)也是對象,是可調(diào)用對象,函數(shù)可以作為普通變量,也可以作為函數(shù)的參數(shù)、返回值,這也是python高階函數(shù)的語法基礎(chǔ)。
通常我們說的Python高階函數(shù)指的是函數(shù)的參數(shù)類型為函數(shù),或者函數(shù)的返回值類型為函數(shù),Python中常用的高階函數(shù)有map、filter、reduce、partial。
高階函數(shù)
概念
在數(shù)學(xué)和計算機(jī)科學(xué)中,高階函數(shù)應(yīng)當(dāng)是至少滿足下面一個條件的函數(shù):
- 接受一個或多個函數(shù)作為參數(shù)
- 輸出一個函數(shù)
數(shù)學(xué)概念 y=f(g(x))
# 高階函數(shù),例1:
def counter(base):
def inc(step=1): # 局部變量
nonlocal base
base += step
return base
return inc # 局部變量 inc函數(shù)對象,每次賦值即重新定義
c1 = counter(5)
c2 = counter(5)
print(c1, c2) #c1、c2引用的是不同的函數(shù)對象
print(c1(), c2()) # 6 6
print(c1() == c2()) # True,函數(shù)對象的返回值相等
print(c1== c2) # False,函數(shù)每次調(diào)用都不一樣
‘’'
<function counter.<locals>.inc at 0x106ed0540> <function counter.<locals>.inc at 0x106ed0b80>
6 6
True
False
‘''
def inc(step=1):
return step
def counter():
return inc # 返回全局變量 inc
c1 = counter()
c2 = counter()
print(c1, c2) #c1和c2引用的是同一個函數(shù)對象
print(c1 == c2) # True,因為全局變量 inc 不消亡
‘’'
<function inc at 0x10eb68540> <function inc at 0x10eb68540>
True
‘''
?高階函數(shù)特點(diǎn)
Python 高階函數(shù)的特點(diǎn):
- 函數(shù)是對象類型(Object type)的實例
- 可以將函數(shù)存儲在變量中
- 可以將函數(shù)作為參數(shù)傳遞給另一個函數(shù)
- 您可以從函數(shù)返回函數(shù)
- 您可以將它們存儲在數(shù)據(jù)結(jié)構(gòu)中,如哈希表、列表等…
函數(shù)作為對象
在 Python中,可以將函數(shù)分配給變量,此賦值不調(diào)用函數(shù),而是創(chuàng)建對該函數(shù)的引用??紤]下面的例子,以便更好地理解。
def shout(text):
return text.upper()
print(shout('Hello'))
# HELLO
# 將函數(shù)賦值給變量
yell = shout
print(yell('Hello'))
# HELLO
在上面的示例中,一個函數(shù)對象被 shout 引用,并創(chuàng)建指向它的第二個名稱 yell。
將函數(shù)作為參數(shù)傳遞給其他函數(shù)類似于 Python 中的對象,因此,它們可以作為參數(shù)傳遞給其他函數(shù)。請考慮下面的示例,在這里我們創(chuàng)建了一個函數(shù) greet,它將函數(shù)作為參數(shù)。
def shout(text):
return text.upper()
def whisper(text):
return text.lower()
def greet(func):
# 將函數(shù)存儲在變量中
greeting = func("Hi, I am created by a function \
passed as an argument.")
print(greeting)
greet(shout)
# HI, I AM CREATED BY A FUNCTION PASSED AS AN ARGUMENT.
greet(whisper)
# hi, i am created by a function passed as an argument.
返回函數(shù)
由于函數(shù)是對象,我們也可以從另一個函數(shù)返回一個函數(shù)。在下面的示例中,create_adder 函數(shù)返回 adder 函數(shù)。
# 來說明函數(shù)可以返回另一個函數(shù)
def create_adder(x):
def adder(y):
return x + y
return adder
add_15 = create_adder(15)
print(add_15(10))
# 25
這個特性正好可以運(yùn)用到下邊的裝飾器的思想中。
裝飾器
裝飾器是 Python 中最常用的高階函數(shù)。它允許程序員修改函數(shù)或類的行為。裝飾器允許我們包裝另一個函數(shù),以擴(kuò)展包裝函數(shù)的行為,而無需永久修改它。在 Decorators 中,函數(shù)作為參數(shù)被放入另一個函數(shù)中,然后在包裝器函數(shù)中調(diào)用。
裝飾器的編寫方法為:
@gfg_decorator
def hello_decorator():
...
# 上述代碼相當(dāng)于:
def hello_decorator():
...
hello_decorator = gfg_decorator(hello_decorator)
在上面的代碼中,gfg_decorator 是一個可調(diào)用函數(shù),將在另一個可調(diào)用函數(shù) hello_decorator 函數(shù)的頂部添加一些代碼,并返回包裝器函數(shù)(wrapper function)。
# 定義一個裝飾器
def hello_decorator(func):
# inner1 is a Wrapper function in
# which the argument is called
# inner function can access the outer local
# functions like in this case "func"
def inner1():
print("Hello, this is before function execution")
# calling the actual function now
# inside the wrapper function.
func()
print("This is after function execution")
return inner1
# defining a function, to be called inside wrapper
def function_to_be_used():
print("This is inside the function !!")
# passing 'function_to_be_used' inside the
# decorator to control its behavior
function_to_be_used = hello_decorator(function_to_be_used)
# calling the function
function_to_be_used()
# 輸出
'''
Hello, this is before function execution
This is inside the function !!
This is after function execution
'''
閉包
說到高階函數(shù),就不得不提閉包,先來看一下Python中閉包的定義:
如果在一個內(nèi)部函數(shù)里,對在外部作用域(但不是在全局作用域)的變量進(jìn)行引用,那么內(nèi)部函數(shù)就被認(rèn)為是閉包(closure)。
def closure():
x = 5
def sub():
return x * x
return sub
如上,在內(nèi)部函數(shù)sub
中包含了對函數(shù)closure
中局部變量x
的引用,這就是閉包。
filter()
基本定義
filter(function or None, iterable) --> filter object
- function or None
# 過濾操作執(zhí)行的函數(shù) - iterable
# 需要過濾的序列
作用:過渡序列中不符合條件的元素。
filter有兩個參數(shù),第1參數(shù)可以是函數(shù),也可以是None.
當(dāng)?shù)?個參數(shù)是函數(shù)的時候,將第2個參數(shù)中每個元素進(jìn)行計算。
當(dāng)?shù)?個參數(shù)是None時,直接將第二個參數(shù)中為True的值篩選出來。
filter() 方法返回一個迭代器(filter 對象),該迭代器通過了 iterable 中每個元素的函數(shù)檢查,返回的是原序列中的值,非布爾值。
函數(shù)使用
lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 得到列表中的偶數(shù)
def func(x):
return x % 2 == 0
result = filter(func, lst)
print(list(result))
# 輸出:
[2, 4, 6, 8, 10]
使用匿名函數(shù)
lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(list(filter(lambda x: x % 2 == 0, lst)))
如果未定義函數(shù)時,相當(dāng)于操作if <元素>,即:
filter(function, iterable) 相當(dāng)于一個生成器表達(dá)式,當(dāng) function 不是 None 的時候為 (item for item in iterable if function(item));function 是 None 的時候為 (item for item in iterable if item) 。
[*filter(None,[1,False,3])]
# [1, 3]
filter 對象
以值為偶數(shù)(除以2時余數(shù)為0)時返回 True 的 lambda(匿名函數(shù))為例:
l = [-2, -1, 0, 1, 2]
print(filter(lambda x: x % 2 == 0, l))
# <filter object at 0x10bb38580>
print(type(filter(lambda x: x % 2 == 0, l)))
# <class 'filter'>
?返回 filter 型的對象,即使直接 print(),也不輸出內(nèi)容的值,可以用 for 循環(huán)等方式取出來。
for i in filter(lambda x: x % 2 == 0, l):
print(i)
# -2
# 0
# 2
# 篩選大于 5 的值
f = filter(lambda x: x>5, [2,3,5,7,9])
f # <filter at 0x7fe33ea36730>
list(f)
# [7, 9]
# 函數(shù)為 None
f = filter(None, [2,False,5,None,9])
list(f)
# [2, 5, 9]
# list of letters
letters = ['a', 'b', 'd', 'e', 'i', 'j', 'o']
# function that filters vowels
def filter_vowels(letter):
vowels = ['a', 'e', 'i', 'o', 'u']
if(letter in vowels):
return True
else:
return False
filtered_vowels = filter(filter_vowels, letters)
print('The filtered vowels are:')
for vowel in filtered_vowels:
print(vowel)
'''
The filtered vowels are:
a
e
i
o
'''
列表推導(dǎo)式替代
?可以用列表表達(dá)式實現(xiàn)它的功能:
l = [-2, -1, 0, 1, 2]
[x for x in l if x % 2 == 0]
# [-2, 0, 2]
[x for x in l if x % 2 != 0]
# [-1, 1]
l_s = ['apple', 'orange', 'strawberry']
[x for x in l_s if x.endswith('e')]
# ['apple', 'orange']
[x for x in l_s if not x.endswith('e')]
# ['strawberry']
l = [-2, -1, 0, 1, 2]
[x for x in l if x]
# [-2, -1, 1, 2]
l_2d = [[0, 1, 2], [], [3, 4, 5]]
[x for x in l_2d if x]
# [[0, 1, 2], [3, 4, 5]]
filterfalse()
itertools.filterfalse()
?,只有 function 返回 false 時才選取 iterable 中元素的補(bǔ)充函數(shù)。和filter()
函數(shù)的篩選機(jī)制相反。
import itertools
foo = itertools.filterfalse(lambda x: x%2==0, [1,3,1,5,6,8,1])
[*foo]
# [1, 3, 1, 5, 1]
map()
基本定義
map(func, *iterables) --> map object
- function
# 序列中的每個元素需要執(zhí)行的操作, 可以是匿名函數(shù) - *iterables
# 一個或多個序列
作用:對可迭代對象的每一個元素作為函數(shù)的參數(shù)進(jìn)行運(yùn)算,然后將其添加到一個新的對象中返回。
返回一個將 function 應(yīng)用于 iterable 中每一項并輸出其結(jié)果的迭代器。 如果傳入了額外的 iterable 參數(shù),function 必須接受相同個數(shù)的實參并被應(yīng)用于從所有可迭代對象中并行獲取的項。 當(dāng)有多個可迭代對象時,最短的可迭代對象耗盡則整個迭代就將結(jié)束。
將給定的函數(shù)應(yīng)用于可迭代對象的每一項,并返回結(jié)果列表,返回的結(jié)果是一個 map object(map 對象),可以將 map object 傳遞給 list()(創(chuàng)建列表)、set()(創(chuàng)建集合)等函數(shù)以顯示和應(yīng)用結(jié)果。
單個迭代對象
# 對可迭代對象進(jìn)行2次方運(yùn)算
lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = map(lambda i: i ** 2, lst)
print(list(result))
#輸出
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
多個迭代對象
map支持多個迭代對象,也需要前面的函數(shù)支持多個輸入變量,如果 iterable 的數(shù)量不一樣,則取短板,其余的放棄,如把兩個列表對應(yīng)的元素求和:
r = map(lambda x, y: x + y, [1, 2, 3, 4], [5, 6, 7, 8])
print(list(r)) # 打印結(jié)果:[6, 8, 10, 12]
# 對象不同長度
def add_num(x, y):
return x + y
m = map(add_num, [1,2,3,4], [1, 2])
list(m)
# [2, 4]
startmap()?
對于函數(shù)的輸入已經(jīng)是參數(shù)元組的情況,可以 使用 itertools.starmap() 操作。它創(chuàng)建一個迭代器,使用從可迭代對象中獲取的參數(shù)來計算該函數(shù)。當(dāng)參數(shù)對應(yīng)的形參已從一個單獨(dú)可迭代對象組合為元組時(數(shù)據(jù)已被“預(yù)組對”)可用此函數(shù)代替 map()。map() 與 starmap() 之間的區(qū)別可以類比 function(a,b) 與 function(*c) 的區(qū)別。
import itertools
t = [(2,5), (3,4)]
# 元組內(nèi)操作(相乘)
sm = itertools.starmap(lambda x,y: x*y, t)
list(sm)
# [10, 12]
? 列表推導(dǎo)式替代
map 的功能可以用列表表達(dá)式來代替:
l = [-2, -1, 0]
[abs(x) for x in l]
# [2, 1, 0]
[x**2 for x in l]
# [4, 1, 0]
l_1 = [1, 2, 3]
l_2 = [10, 20, 30]
[x * y for x, y in zip(l_1, l_2)]
# [10, 40, 90]
在大多數(shù)情況下,與 map 相比,使用列表生成器式更簡潔明了,但也有人認(rèn)為 map 作為高階函數(shù),能更加突出函數(shù),弱化了循環(huán)的表達(dá),讓處理邏輯看起來更加明顯。?
NumPy 代替
在數(shù)據(jù)科學(xué)中,不需要按 map 模式的計算,兩個序列之間的操作被認(rèn)為是一個矩陣計算,NumPy 可以非常好地完成這些,比 map() 和列表表示更為明確。
import numpy as np
a = np.array([-2, -1, 0])
print(np.abs(a))
# [2 1 0]
print(a**2)
# [4 1 0]
a_1 = np.array([1, 2, 3])
a_2 = np.array([10, 20, 30])
print(a_1 * a_2)
# [10 40 90]
對于大規(guī)模的列表的處理和復(fù)雜的處理NumPy更快。NumPy還提供各種函數(shù),所以在進(jìn)行以數(shù)值排列為對象的處理的情況下可以嘗試一下。
reduce()
基本定義
reduce(function, sequence[, initial]) -> value
- function
# 函數(shù), 序列中的每個元素需要執(zhí)行的操作, 可以是匿名函數(shù) - sequence
# 需要執(zhí)行操作的序列 - initial
# 可選,初始參數(shù)
作用:reduce把一個函數(shù)作用在一個序列[x1, x2, x3…]上,這個函數(shù)必須接收兩個參數(shù),reduce把結(jié)果繼續(xù)和序列的下一個元素做累積計算,其效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
每次函數(shù)計算的結(jié)果繼續(xù)和序列的下一個元素做累積計算。
可以理解為 reduce 中的第一個參數(shù) function 是一個函數(shù),它有兩個變量(function 的變量),iterable 是一個序列,function 第一次執(zhí)行時,按順序先取兩個傳入執(zhí)行,得到一個結(jié)果,然后再將這個結(jié)果與 iterable 中的下一個值(還是兩個變量)傳入 function 執(zhí)行,如此反復(fù)直到 iterable 里的值取完為止,最終就能得到一個終極的返回值。
例如,reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) 是計算 ((((1+2)+3)+4)+5) 的值。 左邊的參數(shù) x 是積累值而右邊的參數(shù) y 則是來自 iterable 的更新值。
簡單使用
要想使用,必需導(dǎo)入functools模塊。
# 求列表元素之和
import functools
lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def func(a, b):
return a + b
result = functools.reduce(func, lst)
print(result)
輸出:
55
?使用匿名函數(shù):
import functools
lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(functools.reduce(lambda x, y: x + y, lst))
如果存在可選項 initializer,它會被放在參與計算的可迭代對象的條目之前,并在可迭代對象為空時作為默認(rèn)值。 如果沒有給出 initializer 并且 iterable 僅包含一個條目,則將返回第一項。
累積相除:
from functools import reduce
from operator import truediv
reduce(truediv, [4, 3, 2, 1])
reduce(lambda x,y: x/y, [4, 3, 2, 1])
# 0.6666666666666666
設(shè)定初始值
指定 initializer 參數(shù)時,第一次執(zhí)行時,函數(shù)的第一個參數(shù)傳入此值,第二個參數(shù)為序列的第一個值:
from functools import reduce
from operator import truediv
reduce(truediv, [10, 5, 2], 50)
# 0.5
?還可以把初始值當(dāng)做輸出的提示部分:?
# 設(shè)定初始參數(shù):
s = reduce(lambda x, y: x + y, ['1', '2', '3', '4', '5'], "數(shù)字 = ")
print(s)
# print out: 數(shù)字 = 12345
如果 initializer 沒有指定,序列也會空,則會報錯:
#當(dāng)序列為空時,則返回就是 initializer 的值
reduce(truediv, [], 50)
# 50
reduce(truediv, [])
# TypeError: reduce() of empty iterable with no initial value
性能和可讀性
Python的 reduce() 的性能可能非常差,因為它通過多次調(diào)用函數(shù)來工作。這會使您的代碼變得緩慢和低效。當(dāng)您將 reduce() 用于復(fù)雜的用戶定義函數(shù)或 lambda 函數(shù)時,使用 reduce() 還會影響代碼的可讀性。
Python 提供了一系列工具,可以優(yōu)雅地替換 reduce(),至少在其主要用例中是如此。主要有:
- 盡可能使用專用函數(shù)解決 Python 的 reduce() 的用例。函數(shù)(如 sum、all、any、max、min、len、math.prod等)將使您的代碼更快、更可讀、更易于維護(hù),并且更具 Python 風(fēng)格。
- 使用 reduce() 時避免使用復(fù)雜的用戶定義函數(shù)。這些類型的函數(shù)會使代碼難以閱讀和理解。您可以改為使用顯式且可讀的 for 循環(huán)。
- 使用reduce() 時避免使用復(fù)雜的 lambda 函數(shù),它們還可能使您的代碼無法閱讀和混淆。
第二點(diǎn)和第三點(diǎn)是 Guido (Python 之父,專門撰文討論過 reduce 在 Python 3 的去留問題)自己的擔(dān)憂,他說:
所以現(xiàn)在 reduce() 這實際上是我最討厭的一個,因為除了一些涉及?
+
?或?*
?的示例外,幾乎每次我看到帶有非平凡函數(shù)參數(shù)的 reduce() 調(diào)用時,我都需要抓起紙筆來繪制實際輸入到該函數(shù)中的內(nèi)容,然后才能理解 reduce() 應(yīng)該做什么。所以在我看來,reduce() 的適用性非常局限于關(guān)聯(lián)運(yùn)算符,在所有其他情況下,最好顯式寫出積累循環(huán)。(來源:https://www.artima.com/weblogs/viewpost.jsp?thread=98196)
?以下是用內(nèi)置函數(shù)與reduce() 方法的性能對比:
from timeit import timeit
print('sum()', timeit('sum(range(100))'))
print('reduce()', timeit('reduce(lambda x,y: x+y, range(100))',
setup='from functools import reduce'))
‘’'
sum() 1.5165458140254486
reduce() 8.190408975002356
‘''
如果您打算使用 reduce() 來解決問題,那么與使用專用內(nèi)置函數(shù)的代碼相比,您的代碼將慢得多,內(nèi)置函數(shù)比如求和問題(sum)最具可讀性和 python 風(fēng)格的解決方案。
sorted()
?基本定義
sorted(iterable,key,reverse)
- iterable
# 序列 - key
# 可以用來計算的排序函數(shù)。 - reverse
# 排序規(guī)則,reverse = True 降序,reverse = False 升序(默認(rèn))。
其中,iterable是可迭代對象。
key可選參數(shù),可以接收函數(shù)來實現(xiàn)自定義的排序,默認(rèn)為None(直接比較)。
reverse:排序規(guī)則,為一個布爾值,reverse = True 降序 , reverse = False 升序(默認(rèn))。
# 默認(rèn)情況下,對字符串排序,是按照ASCII的大小比較的
lst = ['bb', 'aaaa', 'c', 'ddddddddd', 'fff']
print(sorted(lst))
輸出:
['aaaa', 'bb', 'c', 'ddddddddd', 'fff']
#對列表按照int值排序
lst = [2, 5, '1', 3, '6', '4']
print(sorted(lst, key=int))
輸出:
['1', 2, 3, '4', 5, '6']
key 函數(shù)參數(shù)
對于更復(fù)雜的自定義排序,sorted() 采用可選的 key 參數(shù)指定一個函數(shù),該函數(shù)在比較之前轉(zhuǎn)換每個元素。key 函數(shù)接受 1 個值并返回1個值,返回的函數(shù)計算值(proxy)值用于排序中的比較。
例如,對于字符串列表,指定?key=len
(內(nèi)置的?len()
?函數(shù))按長度對字符串進(jìn)行排序,從最短到最長。排序?qū)γ總€字符串調(diào)用?len()
?,以獲取代理長度值列表,然后使用這些代理值進(jìn)行排序。
strs = ['ccc', 'aaaa', 'd', 'bb']
sorted(strs, key=len)
# ['d', 'bb', 'ccc', 'aaaa']
可以自定義一個函數(shù),該函數(shù)接受一個值(這個值是序列中的每個元素),并返回代理值以指導(dǎo)排序。因此,key 傳入的是一個可調(diào)用對象,這個對接接受序列中的元素。
# 不區(qū)分大小寫的字符串比較
sorted("This is a test string from Andrew".split(), key=str.lower)
# ['a', 'Andrew', 'from', 'is', 'string', 'test', 'This']
# 使用第二個元素進(jìn)行排序
def take_second(elem):
return elem[1]
# 隨便給一個列表
random = [(2, 2), (3, 4), (4, 1), (1, 3)]
# 帶關(guān)鍵字的排序列表
sorted_list = sorted(random, key=take_second)
# 打印 list
sorted_list
# [(4, 1), (2, 2), (1, 3), (3, 4)]
多健排序
假設(shè)我們有以下列表:
# 科學(xué)奧林匹克競賽中學(xué)生信息的嵌套列表
# 列表元素:(學(xué)生姓名,滿分100分,年齡)
participant_list = [
('Alison', 50, 18),
('Terence', 75, 12),
('David', 75, 20),
('Jimmy', 90, 22),
('John', 45, 12)
]
我們希望對列表進(jìn)行排序,使得分最高的學(xué)生位于開始位置,如果學(xué)生的分?jǐn)?shù)相等,則必須對他們進(jìn)行排序,以便年輕的參與者排在第一位。
我們可以通過返回元組而不是數(shù)字來實現(xiàn)這種多鍵排序。
兩個元組可以通過從第一個元組開始比較它們的元素來進(jìn)行比較。如果存在聯(lián)系(元素相等),則比較第二個元素,依此類推。
>>> (1,3) > (1, 4)
False
>>> (1, 4) < (2,2)
True
>>> (1, 4, 1) < (2, 1)
True
讓我們使用這個邏輯來構(gòu)建排序邏輯。
# Nested list of student's info in a Science Olympiad
# List elements: (Student's Name, Marks out of 100 , Age)
participant_list = [
('Alison', 50, 18),
('Terence', 75, 12),
('David', 75, 20),
('Jimmy', 90, 22),
('John', 45, 12)
]
def sorter(item):
# 因為最高分在先,所以最小錯誤=最高分
error = 100 - item[1]
age = item[2]
return (error, age)
sorted(participant_list, key=sorter)
# [('Jimmy', 90, 22), ('Terence', 75, 12), ('David', 75, 20), ('Alison', 50, 18), ('John', 45, 12)]
?由于排序邏輯函數(shù)很小,可以放在一行中,所以lambda函數(shù)在鍵內(nèi)使用,而不是傳遞單獨(dú)的函數(shù)名。上述程序可通過以下方式使用lambda函數(shù)編寫:
# Nested list of student's info in a Science Olympiad
# List elements: (Student's Name, Marks out of 100 , Age)
participant_list = [
('Alison', 50, 18),
('Terence', 75, 12),
('David', 75, 20),
('Jimmy', 90, 22),
('John', 45, 12)
]
sorted(participant_list, key=lambda item: (100-item[1], item[2]))
# [('Jimmy', 90, 22), ('Terence', 75, 12), ('David', 75, 20), ('Alison', 50, 18), ('John', 45, 12)]
排序原理
對字符的排序是按照 unicode 的碼位順序進(jìn)行排序的,但有一些細(xì)節(jié)實現(xiàn)比較復(fù)雜。排序算法只使用項目之間的?<
?比較。雖然定義一個__lt__()
?(小于)方法就足以進(jìn)行排序,但 PEP 8 建議實現(xiàn)所有六個比較功能("<" | ">" | "==" | ">=" | "<=" | "!="
)的特殊方法,這將有助于避免將相同的數(shù)據(jù)與依賴于不同底層方法的其他排序工具(如?max()
?)一起使用時出現(xiàn)錯誤。實現(xiàn)所有六個比較功能的特殊方法也有助于避免混合類型比較的混淆,混合類型比較可以調(diào)用?__gt__()
?方法。
# vowels list
py_list = ['e', 'a', 'u', 'o', 'i']
sorted(py_list)
# ['a', 'e', 'i', 'o', 'u']
# string
py_string = 'Python'
sorted(py_string)
# ['P', 'h', 'n', 'o', 't', 'y']
# vowels tuple
py_tuple = ('e', 'a', 'u', 'o', 'i')
sorted(py_tuple)
# ['a', 'e', 'i', 'o', 'u']
請注意,在所有情況都會返回排序后的列表,不認(rèn)原數(shù)據(jù)是什么結(jié)構(gòu)。
按降序排序
sorted() 函數(shù)接受一個 reverse 參數(shù)作為可選參數(shù),設(shè)置 reverse=True 將按降序?qū)?iterable 進(jìn)行排序。
# set
py_set = {'e', 'a', 'u', 'o', 'i'}
print(sorted(py_set, reverse=True))
# ['u', 'o', 'i', 'e', 'a']
# dictionary
py_dict = {'e': 1, 'a': 2, 'u': 3, 'o': 4, 'i': 5}
sorted(py_dict, reverse=True)
# ['u', 'o', 'i', 'e', 'a']
# frozen set
frozen_set = frozenset(('e', 'a', 'u', 'o', 'i'))
sorted(frozen_set, reverse=True)
# ['u', 'o', 'i', 'e', 'a']
sort()與sorted的區(qū)別
- sort() 函數(shù)只適用于列表排序,而sorted()函數(shù)適用于任意可以迭代的對象排序。
- sort() 函數(shù)排序會改變原有的待排序列表,而sorted()函數(shù)則不會改變。所以在使用列表進(jìn)行排序時,需要考慮是否需要保存原列表,如果無需保存原列表,則優(yōu)先使用sort() 節(jié)省內(nèi)存空間,提高效率。
partial()
柯里化基本概念
partial 函數(shù)允許我們復(fù)刻函數(shù)的某些參數(shù)并生成新函數(shù)。函數(shù)在執(zhí)行時,要帶上所有必要的參數(shù)進(jìn)行調(diào)用。但是,有時參數(shù)可以在函數(shù)被調(diào)用之前提前獲知。這種情況下,一個函數(shù)有一個或多個參數(shù)預(yù)先就能用上,以便函數(shù)能用更少的參數(shù)進(jìn)行調(diào)用。簡單說就是局部套用一個函數(shù),讓廣泛功能的函數(shù)簡單化、單一化。這是一個 柯里化 過程。在數(shù)學(xué)和計算機(jī)科學(xué)中,柯里化是一種將使用多個參數(shù)的一個函數(shù)轉(zhuǎn)換成一系列使用一個參數(shù)的函數(shù)的技術(shù)。
簡單說 partial 把某個函數(shù)的某個參數(shù)固定,從而構(gòu)造出一個新的函數(shù)來。返回的這個新函數(shù)對象是 partial 對象,下文有介紹。
在一些情況下, 我們在設(shè)計 Python 函數(shù)的時候, 會給它設(shè)定非常豐富的功能, 這樣我們就可以定義一個非常強(qiáng)大的函數(shù)。 與此同時帶來的問題是使用上的不方便, 因為有可能我們需要傳遞非常多的參數(shù)才能換成我們想要的功能。這時候 partial 函數(shù)就可以讓我們在這個強(qiáng)大的函數(shù)中派生出對應(yīng)的具體功能。
在計算機(jī)科學(xué)中,柯里化(英語:Currying),又譯為科里化、卡瑞化、或加里化,是把接受多個參數(shù)的函數(shù)變換成接受一個單一參數(shù)(最初函數(shù)的第一個參數(shù))的函數(shù),并且返回接受余下的參數(shù)而且返回結(jié)果的新函數(shù)的技術(shù)。這個技術(shù)由克里斯托弗·斯特雷奇以邏輯學(xué)家哈斯凱爾·加里命名的,盡管它是Moses Sch?nfinkel和戈特洛布·弗雷格發(fā)明的。
例如,傳入百、十、個位數(shù)字生成數(shù)字:
from functools import partial
# 常規(guī)函數(shù)
def add(a, b, c):
return 100 * a + 10 * b + c
# b=1,c=2 的部分函數(shù)
add_part = partial(add, c = 2, b = 1)
# 調(diào)用 partial 函數(shù)
add_part(3)
# 312
例如,我們有加法函數(shù)(實際上我們有 sum,不需要它),我們可以將它派生出不同的加法函數(shù):
from functools import partial
def add(a,b):
return a + b
def add2number(x,y,z):
return x + y + z
# a 固定值為 2
add2 = partial(add,2)
add2(1)
# 3
# 將 x 固定為 1,y 值固定為 2
add3 = partial(partial(add2number,1), 2)
add3(1)
# 4
語法定義
完整語法為:functools.partial(func, /, *args, **keywords)
,返回一個新的 partial 對象(部分對象,見下文),又稱偏函數(shù),主要用途是減少可調(diào)用對象的參數(shù)個數(shù),當(dāng)被調(diào)用時其行為類似于 func 附帶位置參數(shù) args 和關(guān)鍵字參數(shù) keywords 被調(diào)用。 如果為調(diào)用提供了更多的參數(shù),它們會被附加到 args。 如果提供了額外的關(guān)鍵字參數(shù),它們會擴(kuò)展并重載 keywords。 這個函數(shù)是使用 C 而不是 Python 實現(xiàn)的,大致等價于:
def partial(func, /, *args, **keywords):
def newfunc(*fargs, **fkeywords):
newkeywords = {**keywords, **fkeywords}
return func(*args, *fargs, **newkeywords)
newfunc.func = func
newfunc.args = args
newfunc.keywords = keywords
return newfunc
partial() 會被“凍結(jié)了”一部分函數(shù)參數(shù)和/或關(guān)鍵字的部分函數(shù)應(yīng)用所使用,從而得到一個具有簡化簽名的新對象。 例如,partial() 可用來創(chuàng)建一個行為類似于 int() 函數(shù)的可調(diào)用對象,其中 base 參數(shù)默認(rèn)為二:
from functools import partial
basetwo = partial(int, base=2)
basetwo.__doc__ = 'Convert base 2 string to an int.'
basetwo('10010')
# 18
在類中使用
在類中如果想實現(xiàn) partial 類似的派生方法的話,可以使用 functools.partialmethod 方法,如:
import functools
class RGB(object):
def __init__(self, red, blue, green):
super(RGB, self).__init__()
self._red = red
self._blue = blue
self._green = green
def _color(self, type):
return getattr(self, type)
red = functools.partialmethod(_color, type='_red')
blue = functools.partialmethod(_color, type='_blue')
green = functools.partialmethod(_color, type='_green')
?partial 對象
partial 對象是由 partial() 創(chuàng)建的可調(diào)用對象。 它們具有三個只讀屬性:
- partial.func:一個可調(diào)用對象或函數(shù)。 對 partial 對象的調(diào)用將被轉(zhuǎn)發(fā)給 func 并附帶新的參數(shù)和關(guān)鍵字。
- partial.args:最左邊的位置參數(shù)將放置在提供給 partial 對象調(diào)用的位置參數(shù)之前。
- partial.keywords:當(dāng)調(diào)用 partial 對象時將要提供的關(guān)鍵字參數(shù)。
partial 對象與 function 對象的類似之處在于它們都是可調(diào)用、可弱引用的對象并可擁有屬性。 但兩者也存在一些重要的區(qū)別。 例如前者不會自動創(chuàng)建?__name__
?和?__doc__
?屬性。 而且,在類中定義的 partial 對象的行為類似于靜態(tài)方法,并且不會在實例屬性查找期間轉(zhuǎn)換為綁定方法。
如上例 basetwo 就是一個 partial 對象:
from functools import partial
basetwo = partial(int, base=2)
basetwo
# functools.partial(<class 'int'>, base=2)
basetwo.args
# ()
basetwo.func
# int
basetwo.keywords
# {'base': 2}
偏函數(shù)可用于從常規(guī)函數(shù)派生專用函數(shù),從而幫助我們重用代碼。
這個特性類似于C++中的綁定。
Currying 是將一個具有n個參數(shù)的函數(shù)劃分為 n 個具有一個參數(shù)的連續(xù)函數(shù)。部分應(yīng)用程序是使用一些參數(shù)“預(yù)填充”函數(shù)( 'pre-filling' a function),然后返回參數(shù)數(shù)量較少的函數(shù)。
partialmethod()?
Python 內(nèi)置模塊 functools 的一個高階函數(shù) partialmethod 與 partial() 偏函數(shù) 類似,partialmethod 偏方法也是在對象里對已有的方法進(jìn)行派生,將功能復(fù)雜的方法衍生出簡單功能的方法。這是從 Python 3.4 版新功能。
偏函數(shù)基本概念
對于python 偏函數(shù)partial理解運(yùn)用起來比較簡單,就是對原函數(shù)某些參數(shù)設(shè)置默認(rèn)值,生成一個新函數(shù)。而如果對于類方法,因為第一個參數(shù)是 self,使用 partial 就會報錯了。
class functools.partialmethod(func, /, *args, **keywords)
返回一個新的 partialmethod 描述器,其行為類似 partial 但它被設(shè)計用作方法定義而非直接用作可調(diào)用對象。
func 必須是一個 descriptor 或可調(diào)用對象(同屬兩者的對象例如普通函數(shù)會被當(dāng)作描述器來處理)。
當(dāng) func 是一個描述器(例如普通 Python 函數(shù), classmethod(), staticmethod(), abstractmethod() 或其他 partialmethod 的實例)時, 對?__get__
?的調(diào)用會被委托給底層的描述器,并會返回一個適當(dāng)?shù)?部分對象 作為結(jié)果。
當(dāng) func 是一個非描述器類可調(diào)用對象時,則會動態(tài)創(chuàng)建一個適當(dāng)?shù)慕壎ǚ椒ā?當(dāng)用作方法時其行為類似普通 Python 函數(shù):將會插入 self 參數(shù)作為第一個位置參數(shù),其位置甚至?xí)幱谔峁┙o partialmethod 構(gòu)造器的 args 和 keywords 之前。
定義類方法
from functools import partialmethod
class Cell:
def __init__(self):
self._alive = False
@property
def alive(self):
return self._alive
def set_state(self, state):
self._alive = bool(state)
set_alive = partialmethod(set_state, True)
set_dead = partialmethod(set_state, False)
print(type(partialmethod(set_state, False)))
# <class 'functools.partialmethod'>
c = Cell()
c.alive
# False
c.set_alive()
c.alive
# True
c.set_alive() 和 c.set_dead() 作為 Cell 的方法,更加直觀,省去專門的定義代碼。
派生三方庫方法
pandas 的 apply 方法 默認(rèn) axis=0 按行去操作,我們需要一個派生一個按列的的操作:
from functools import partialmethod
import pandas as pd
import pandas._testing as tm
df = tm.makeDataFrame()
df
'''
A B C D
D4mjVx3GtT 1.808966 -1.199819 -0.779483 -0.566463
ylYk4vm4MZ -1.509050 -0.361186 0.486100 0.021837
tDbfPX8Eva 0.141416 0.397220 0.172930 -0.504479
...
19tYkM1qrE -0.617952 1.137066 -0.962393 0.982731
mpQIQkifPC 0.874417 0.226630 0.739977 -0.786624
'''
# 使用原函數(shù)
df.apply(sum)
'''
A 4.621611
B 8.959269
C 0.126089
D 2.828309
dtype: float64
'''
# 定義偏方法
pd.DataFrame.apply_col = partialmethod(pd.DataFrame.apply, axis=1)
# 使用偏方法
df.apply_col(sum)
'''
D4mjVx3GtT -0.736799
ylYk4vm4MZ -1.362299
tDbfPX8Eva 0.207087
...
19tYkM1qrE 0.539452
mpQIQkifPC 1.054401
dtype: float64
'''
partial 對象
partialmethod 派生出的新方法是一個 partial 對象,它有 args、func、keywords 三個屬性,如在上例中:文章來源:http://www.zghlxwxcb.cn/news/detail-684929.html
pd.DataFrame.apply_col
# functools.partial(<function DataFrame.apply at 0x7fdb3057ae60>, axis=1)
pd.DataFrame.apply_col.args
# ()
pd.DataFrame.apply_col.func
# <function pandas.core.frame.DataFrame.apply(self, func: 'AggFuncType',
# axis: 'Axis' = 0, raw: 'bool' = False, result_type=None, args=(), **kwargs)>
pd.DataFrame.apply_col.keywords
# {'axis': 1}
與 partial 偏函數(shù)的區(qū)別
?partialmethod 針對對象中的方法進(jìn)行派生,partial 是對獨(dú)立的函數(shù)進(jìn)行派生。偏函數(shù) partial 對原函數(shù)某些參數(shù)設(shè)置默認(rèn)值,生成一個新函數(shù)。而如果對于類方法,因為第一個參數(shù)是 self,使用 partial 就會報錯,因此要使用 partialmethod。文章來源地址http://www.zghlxwxcb.cn/news/detail-684929.html
到了這里,關(guān)于Python高階函數(shù)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!