- First Class Object
函數(shù)在Python中是一等公民,函數(shù)也是對象,可調用的對象历筝,函數(shù)可以作為普通變量、參數(shù)廊谓、返回值等等
一梳猪、 高階函數(shù)
1. 定義
在數(shù)學和計算機科學中,高階函數(shù)應當是至少滿足下面一個條件的函數(shù):
1)接受一個或多個函數(shù)作為參數(shù)
2)輸出一個函數(shù)
2. 例子
簡單計數(shù)器:
def counter(base):
def inc(step=1):
nonlocal base #閉包
base += step
return base
return inc
函數(shù)counter是一個高階函數(shù)蒸痹,因為它輸出了一個函數(shù)inc春弥;
f1 = counter(5)和f2 = counter(5) 具有相等的函數(shù)返回值,f1 == f2為True
f1 = counter 和 f2 = counter返回不同的的函數(shù)對象
3. 內建函數(shù) - 高階函數(shù):
3.1排序
sorted(iterable[, key][, reverse])
功能:返回一個新的列表叠荠,對一個可迭代對象的所有元素排序匿沛,排序規(guī)則為key定義的函數(shù),reverse表示是否排序翻轉
3.2過濾數(shù)據(jù)
filter(function, iterable) --> filter object
功能:過濾可迭代對象的元素榛鼎,返回一個迭代器逃呼,function一個具有一個參數(shù)的函數(shù)鳖孤,返回bool。
例如:過濾出數(shù)列中能被3整除的數(shù)字
list(filter(lambda x: x%3==0, [1,9,55,150,-3,78,28,123]))
3.3映射
map(func, *iterables) --> map object
功能:對多個可迭代對象的元素按照指定的函數(shù)進行映射抡笼,返回一個迭代器
例如:
list(map(lambda x:2*x+1, range(5)))
dict(map(lambda x: (x%5,x) , range(500)))
4. 自定義sorted函數(shù)
仿照內建函數(shù)sorted苏揣,請自行實現(xiàn)一個sort函數(shù)(不使用內建函數(shù)),能夠為列表元素排序
思路:
1)內建函數(shù)sorted函數(shù)是返回一個新的列表推姻,可以設置升序或降序平匈,可以設置一個排序的函數(shù)。自定義的sort函數(shù)也要實現(xiàn)這個功能
2)新建一個列表藏古,遍歷原列表增炭,和新列表的值依次比較決定如何插入到新列表中
#自定義sort函數(shù) - 1
def my_sort(lst):
row = []
for x in lst:
for i,y in enumerate(row):
if x>y: #找到大的插入
row.insert(i,x)
break #不加break的話,會繼續(xù)執(zhí)行第二層for循環(huán)拧晕,隨著元素的不斷加入痹换,一直會是2>1匈棘,第二層for循環(huán)會一直執(zhí)行下去胯盯,直到撐爆內存
else:
row.append(x)
return row
print(my_sort([1,2,3,4,5]))
#自定義sort函數(shù) - 2
def sort(iterable,reverse=False):
row = []
for x in iterable:
for i,y in enumerate(row):
flag = x>y if reverse else x<y
if flag:
row.insert(i,x)
break
else:
row.append(x)
return row
print(sort([4,2,5,1,3]))
#自定義sort函數(shù) - 3
def sort(iterable,key=lambda a,b:a>b):
ret = []
for x in iterable:
for i,y in enumerate(ret):
if key(x,y): #函數(shù)的返回值是bool
ret.insert(i,x)
break
else:
ret.append(x)
return ret
print(sort([4,2,5,1,3]))
#自定義sort函數(shù) - 4
def sort(iterable,reverse=False,key=lambda x,y:x>y):
ret = [] #未來排好序的列表
for x in iterable:
for i,y in enumerate(ret):
if key(x,y):
ret.insert(i,x)
break
else:
ret.append(x)
return ret
sort([4,2,5,1,3])
#自定義sort函數(shù) - 4.1
def sort(iterable,reverse=False,key=lambda x,y:x>y):
ret = [] #未來排好序的列表
for x in iterable:
for i,y in enumerate(ret):
flag = key(x,y) if not reverse else not key(x,y)
if flag:
ret.insert(i,x)
break
else:
ret.append(x)
return ret
sort([4,2,5,1,3])
5. 自定義filter函數(shù)
filter函數(shù)源碼:
class filter(object):
"""
filter(function or None, iterable) --> filter object
Return an iterator yielding those items of iterable for which function(item)
is true. If function is None, return the items that are true.
"""
def __getattribute__(self, *args, **kwargs): # real signature unknown
""" Return getattr(self, name). """
pass
def __init__(self, function_or_None, iterable): # real signature unknown; restored from __doc__
pass
def __iter__(self, *args, **kwargs): # real signature unknown
""" Implement iter(self). """
pass
@staticmethod # known case of __new__
def __new__(*args, **kwargs): # real signature unknown
""" Create and return a new object. See help(type) for accurate signature. """
pass
def __next__(self, *args, **kwargs): # real signature unknown
""" Implement next(self). """
pass
def __reduce__(self, *args, **kwargs): # real signature unknown
""" Return state information for pickling. """
pass
二扩淀、裝飾器
1.柯里化
1,定義:指的是將原來接受兩個參數(shù)的函數(shù)變成新的接受一個參數(shù)的函數(shù)的過程蔫敲。新的函數(shù)返回一個以原有第二個參數(shù)為參數(shù)的函數(shù)饲嗽,如:z = f(x, y) 轉換成 z = f(x)(y)的形式
2,舉例:
將加法函數(shù)柯里化
def add(x, y):
return x + y
轉換如下
def add(x):
def _add(y):
return x+y
return _add
add(5)(6)
通過嵌套函數(shù)就可以把函數(shù)轉換成柯里化函數(shù)
2.裝飾器(無參)
1奈嘿,裝飾器本質上是一個python函數(shù)貌虾,它可以讓其他函數(shù)在不需要做任何代碼變動的前提下增加額外的功能,裝飾器的返回值也是一個函數(shù)對象裙犹。裝飾器經常用于有切面需求的場景尽狠,比如:插入日志、性能測試叶圃、事務處理袄膏、緩存、權限校驗等場景
2掺冠,形成過程
一個加法函數(shù)沉馆,想增強它的功能,能夠輸出被調用過程以及調用的參數(shù)信息
def add(x,y):
return x + y
#增加信息輸出功能
def add(x,y):
print("call add,x + y") #日志輸出到控制臺
return x + y
add(4,5)
#輸出結果
call add,x + y
Out[1]:
9
上面的加法函數(shù)是完成了需求德崭,但是有以下的缺點:
- 打印語句的耦合太高
- 加法函數(shù)屬于業(yè)務功能斥黑,而輸出信息的功能,屬于非業(yè)務功能代碼眉厨,不該放在業(yè)務函數(shù)加法中
所以進一步改進:
def add(x,y):
return x + y
def logger(fn):
print('begin')
x = fn(4,5)
print('end')
return x
print(logger(add))
#輸出結果
begin
end
9
在先前的基礎上做到了業(yè)務分離功能锌奴,但是fn函數(shù)調用傳參是個問題
def add(x,y):
return x + y
def logger(fn,*args,**kwargs):
print('begin')
x = fn(*args,**kwargs)
print('end')
return x
print(logger(add,4,y=5))
解決了傳參問題,進一步改變憾股,柯里化+高階函數(shù):
def add(x,y):
return x + y
def logger(fn):
def wrapper(*args,**kwargs):
print('begin')
x = fn(*args,**kwargs)
print('end')
return x
return wrapper
print(logger(add)(4,y=5))
#或者換種寫法:
add = logger(add)
print(add(x=4,y=5))
最后鹿蜀,引入裝飾器語法糖
def logger(fn):
def wrapper(*args,**kwargs):
print('begin')
x = fn(*args,**kwargs)
print('end')
return x
return wrapper
#裝飾器語法糖
@logger #add = logger(add)
def add(x,y):
return x + y
print(add(4,5))
@logger就是裝飾器的語法
綜上箕慧,我們可以得出:裝飾器(無參)是一個函數(shù),函數(shù)作為它的形參茴恰,返回值也是一個函數(shù)销钝,可以使用@functionname方式,簡化調用琐簇;裝飾器是高階函數(shù),但裝飾器是對傳入函數(shù)的功能的裝飾(功能增強)
3.文檔字符串和帶參裝飾器
Python文檔字符串Documentation Strings
在函數(shù)語句塊的第一行座享,且習慣是多行的文本婉商,所以多使用三引號;慣例是首字母大寫渣叛,第一行寫概述丈秩,空一行,第三行寫詳細描述淳衙;可以使用特殊屬性doc訪問這個文檔
例如:
def add(x,y):
"""This is a function of addition"""
a = x + y
return x + y
print("name={}\ndoc={}".format(add.__name__,add.__doc__)) #注意是雙下劃線
print(help(add))
#輸出結果
name=add
doc=This is a function of addition
Help on function add in module __main__:
add(x, y)
This is a function of addition
None
而如果我們要使用裝飾器的話蘑秽,會發(fā)現(xiàn)原函數(shù)對象的屬性都被替換了,例如:
def logger(fn):
def wrapper(*args,**kwargs):
'I am wrapper'
print('begin')
x = fn(*args,**kwargs)
print('end')
return x
return wrapper
@logger #add = logger(add)
def add(x,y):
'''This is a function for add'''
return x + y
print("name={}, doc={}".format(add.__name__, add.__doc__))
#輸出結果
name=wrapper, doc=I am wrapper #輸出的屬性是wrapper函數(shù)的不是我們想要的add函數(shù)的
但是我們的需求是查看被封裝函數(shù)的屬性箫攀,解決方法為:
def copy_properties(src, dst): # 可以改造成裝飾器
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
def logger(fn):
def wrapper(*args,**kwargs):
'I am wrapper'
print('begin')
x = fn(*args,**kwargs)
print('end')
return x
copy_properties(fn, wrapper)
return wrapper
@logger #add = logger(add)
def add(x,y):
'''This is a function for add'''
return x + y
print("name={}, doc={}".format(add.__name__, add.__doc__))
方法就是通過copy_properties函數(shù)將被包裝函數(shù)的屬性覆蓋掉包裝函數(shù)肠牲,凡是被裝飾的函數(shù)都需要復制這些屬性,這個函數(shù)很通用靴跛,可以將復制屬性的copy_properties函數(shù)構建成裝飾器函數(shù)缀雳,即帶參裝飾器。
def copy_properties(src):
def _copy(dst):
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
return dst
return _copy
def logger(fn):
@copy_properties(fn) #wrapper = copy_properties(fn)(wrapper)
def wrapper(*args,**kwargs):
'I am wrapper'
print('begin')
x = fn(*args,**kwargs)
print('end')
return x
#copy_properties(fn, wrapper)
return wrapper
@logger #add = logger(add)
def add(x,y):
'''This is a function for add'''
return x + y
print("name={}, doc={}".format(add.__name__, add.__doc__))
@copy_properties(fn)帶參裝飾器,它是一個函數(shù),函數(shù)作為它的形參,返回值是一個不帶參的裝飾器函數(shù),使用@functionname(參數(shù)列表)方式調用.可以看做在裝飾器外層又加了一層函數(shù)