高階函數
把函數作為參數傳入,這樣的函數稱為高階函數
from math import sqrt
def test(x, *func):
ret = list(f(x) for f in func)
return ret
print(test(4, abs, sqrt)) # [4, 2.0]
map函數
map函數接收兩個參數,第一個是一個函數,第二個是Iterable,map將傳入的函數依次作用到Iterable中的每個元素,并把結果作為新的Iterator返回
# 將名字變為首字母大寫,其他小寫
def test(elem):
return elem.capitalize()
print(list(map(test, ['adam', 'LISA', 'barT']))) # ['Adam', 'Lisa', 'Bart']
reduce函數
reduce函數接收兩個參數,第一個是一個函數,第二個是一個序列,reduce將傳入的函數依次作用到Iterable中的每個元素,并把每一個元素的結果與下一個元素作累積計算
from functools import reduce
def str2int(s):
def char2num(c):
# 根據dict中的key來獲取value
return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[c]
def f(x, y):
return x * 10 + y
return reduce(f, map(char2num, s))
print(str2int("13579")) # 13579
print(type(str2int("13579"))) # <class 'int'>
上面是我們自己寫的一個str轉int的函數,雖然系統給我們提供了。我們還可以寫一個str轉float的函數來加強reduce與map的理解
def str2float(s):
# 獲取小數點的位置
point = s.index(".")
# 整數部分
num1 = map(int, s[:point])
# 小數部分
num2 = map(int, s[point + 1:])
# 算出小數有多少位
num3 = 1 / 10 ** len(s[point + 1:])
def f(x, y):
return x * 10 + y
return reduce(f, num1) + reduce(f, num2) * num3
print(str2float("126.789")) # 126.789
print(type(str2float("126.789"))) # <class 'float'>
filter函數
filter()
也接收一個函數和一個序列,把傳入的函數依次作用于每一個序列的元素,然后根據返回值是True還是False來決定保留還是丟棄該元素。filter()
函數返回的是一個Iterator
def func(elem):
return elem % 2 == 0
print(list(filter(func, list(range(1, 11))))) # [2, 4, 6, 8, 10]
sorted函數
sorted()
可以對一個序列進行排序,也可以接收一個key來實現自定義的排序
l = [36, 5, -12, 9, -21]
print(sorted(l)) # [-21, -12, 5, 9, 36] 從小到大
print(sorted(l, reverse=True)) # [36, 9, 5, -12, -21] 從大到小
print(sorted(l, key=abs)) # [5, 9, -12, -21, 36] 按絕對值的大小排序
對字符串進行排序。對字符串排序是按照ASCII大小比較的,由于'Z'<'a'
,大寫字母Z會排在小寫字母a的前面,所以可以過字符串的lower方法將先把字符串都變成小寫(也可以都變成大寫),再比較
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88), ('Zoo', 40)]
def by_name(item):
return item[0].lower()
print(sorted(L, key=by_name)) # [('adam', 92), ('Bart', 66), ('Bob', 75), ('Lisa', 88), ('Zoo', 40)]
返回函數
上面學習了將函數作為參數的高階函數,也可以把函數作為結果值返回
def test(*args):
def f():
ret = 0
for item in args:
ret += item
return ret
return f
result = test(1, 3, 5, 7, 9)
# 返回回來的是一個函數,必須調用才能獲取到返回值
print(result()) # 25
print(result) # <function test.<locals>.f at 0x101a7ac80>
我們在函數test中定義了函數f,內部函數f可以引用外部函數test的的參數和局部變量;當函數test返回函數f時,相關參數和變量都保存在返回的函數中,這種稱為閉包(Closure)。但這里有一個問題需要注意:
def test2():
fs = []
for i in range(1, 4):
def f():
return i * i
fs.append(f)
return fs
f1, f2, f3 = test2()
print(f1()) # 9
print(f2()) # 9
print(f3()) # 9
這里的結果為什么都是9?
原因在于返回的函數引用了變量i,但它并非立即執行,等到3個函數都返回時,它們所引用的變量i已經變成了3,因此最終結果都是9。
所以,返回閉包時牢記一點:返回函數不要引用任何循環變量,或者后續會發生變化的變量
若一定要引用循環變量怎么辦呢?可以這樣做:
def test3():
fs = []
def f(x):
def g():
return x * x
return g
for i in range(1, 4):
fs.append(f(i)) # f(i)立即被執行,因此當前i的值被傳入到f()函數中
return fs
f4, f5, f6 = test3()
print(f4()) # 1
print(f5()) # 4
print(f6()) # 9
重新創建了一個函數,用該函數的參數與循環變量當前的值進行綁定,無論后緒循環變量如何變,已綁定的函數參數的值不變
匿名函數
結構:
lambda 參數名 : 表達式 或者 lambda : 表達式
如:
print(list(map(lambda x: x * x, [1, 2, 3]))) # [1, 4, 9]
def test(x, y):
return lambda: x + y
print(test(1, 2)()) # 3
裝飾器
在代碼運行期間動態增加功能的方式稱之為裝飾器(Decorator)
# 調用函數time時,輸出log日志
import functools
def log(func):
# 把原始函數的__name__等屬性復制到wrapper()函數中,否則返回出去的__name__屬性值為wrapper
@functools.wraps(func)
def wrapper(*args, **kw):
print("call %s():" % func.__name__)
return func(*args, **kw)
return wrapper
# 這里相當于執行:time = log(time)
@log
def time():
print("2017-2-5")
time() # call time(): 2017-2-5
print(time.__name__) # time
自定義一個輸出log文本的裝飾器
def log2(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print("%s ,%s():" % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
# 這里相當于執行:time2 = log("execute")(time2)
@log2("execute")
def time2():
print("2017-1-5")
time2() # execute ,time2(): 2017-1-5
print(time2.__name__) # time2
首先執行的是log("execute"),返回decorator,再調用返回的函數,參數就是time2,返回值最終是wrapper函數
偏函數
在學習函數參數類型的時候,為了降低函數的調用難度,我們可以設置默認參數。而偏函數也可以做到。
偏函數的作用就是:把一個函數的某些參數給固定住(也就是設置默認值),返回一個新的函數,調用這個新函數會更簡單
def test(x, sqrt=2):
return x ** sqrt
test2 = functools.partial(test, sqrt=3)
print(test2(3)) # 27