1.定義函數
函數代碼塊以 def 關鍵詞開頭,后接函數標識符名稱和圓括號 ()。
任何傳入參數和自變量必須放在圓括號中間,圓括號之間可以用於定義參數。
函數的第一行語句可以選擇性地使用文檔字符串—用於存放函數說明。
函數內容以冒號起始,並且縮進。
return [表達式] 結束函數,選擇性地返回一個值給調用方。不帶表達式的return相當於返回 None。
可以返回多個值,其實就是一個tuple。
def my_abs(x): if x >= 0: return x else: return -x
空函數:如果想定義一個什么事也不做的空函數,可以用來占位,可以用pass語句:
def nop(): pass
2.參數傳遞
可更改(mutable)與不可更改(immutable)對象
在 python 中,strings, tuples, 和 numbers 是不可更改的對象,而 list,dict 等則是可以修改的對象。
不可變類型:變量賦值 a=5 后再賦值 a=10,這里實際是新生成一個 int 值對象 10,再讓 a 指向它,而 5 被丟棄,不是改變a的值,相當於新生成了a。
可變類型:變量賦值 la=[1,2,3,4] 后再賦值 la[2]=5 則是將 list la 的第三個元素值更改,本身la沒有動,只是其內部的一部分值被修改了。
python 函數的參數傳遞:
不可變類型:類似 c++ 的值傳遞,如 整數、字符串、元組。如fun(a),傳遞的只是a的值,沒有影響a對象本身。比如在 fun(a)內部修改 a 的值,只是修改另一個復制的對象,不會影響 a 本身。
可變類型:類似 c++ 的引用傳遞,如 列表,字典。如 fun(la),則是將 la 真正的傳過去,修改后fun外部的la也會受影響
python 中一切都是對象,嚴格意義我們不能說值傳遞還是引用傳遞,我們應該說傳不可變對象和傳可變對象。
3.參數
調用函數時可使用的正式參數類型:必需參數,關鍵字參數,默認參數,不定長參數等,參數類型也可以組合。
a. 必須參數,也叫位置參數
必需參數須以正確的順序傳入函數。調用時的數量必須和聲明時的一樣。
b.關鍵字參數
關鍵字參數和函數調用關系緊密,函數調用使用關鍵字參數來確定傳入的參數值。
使用關鍵字參數允許函數調用時參數的順序與聲明時不一致,因為 Python 解釋器能夠用參數名匹配參數值。
def printinfo( name, age ): "打印任何傳入的字符串" print ("名字: ", name) print ("年齡: ", age) return #調用printinfo函數 printinfo( age=50, name="runoob" )
c.默認參數
調用函數時,如果沒有傳遞參數,則會使用默認參數。默認參數要放在最后。默認參數必須指向不可變對象。
def printinfo( name, age = 35 ): "打印任何傳入的字符串" print ("名字: ", name) print ("年齡: ", age) return #調用printinfo函數 printinfo( age=50, name="runoob" ) print ("------------------------") printinfo( name="runoob" )
d.不定長參數,可變參數,收集參數
需要一個函數能處理比當初聲明時更多的參數。
加了星號 * 的參數會以元組(tuple)的形式導入,存放所有未命名的變量參數。
果在函數調用時沒有指定參數,它就是一個空元組。我們也可以不向函數傳遞未命名的變量。
如果* 后,還有參數,參數必須用關鍵字傳入。
加了兩個星號 ** 的參數會以字典的形式導入。允許你傳入0個或任意個含參數名的參數,這些關鍵字參數在函數內部自動組裝為一個dict。
def printinfo( arg1, *vartuple ): "打印任何傳入的參數" print ("輸出: ") print (arg1) for var in vartuple: print (var) return # 調用printinfo 函數 printinfo( 10 ) printinfo( 70, 60, 50 )
>>> nums = [1, 2, 3] >>> calc(*nums) 14
def fun(**kwargs):
for x, y in kwargs.items():
print(x,y)
fun(city = "asdasd",beijing = "sadasdw")
4.匿名函數
python 使用 lambda 來創建匿名函數。
所謂匿名,意即不再使用 def 語句這樣標准的形式定義一個函數。
lambda 只是一個表達式,函數體比 def 簡單很多。
lambda的主體是一個表達式,而不是一個代碼塊。僅僅能在lambda表達式中封裝有限的邏輯進去。
lambda 函數擁有自己的命名空間,且不能訪問自己參數列表之外或全局命名空間里的參數。
雖然lambda函數看起來只能寫一行,卻不等同於C或C++的內聯函數,后者的目的是調用小函數時不占用棧內存從而增加運行效率。
sum = lambda arg1, arg2: arg1 + arg2
請參考:https://www.cnblogs.com/kaishirenshi/p/8611358.html
two_sum
=
(
lambda
x, y: x
+
y)(
3
,
4
)
匿名函數也是一個函數對象,也可以把匿名函數賦值給一個變量,再利用變量來調用該函數:
>> f = lambda x: x * x >>> f <function <lambda> at 0x101c6ef28> >>> f(5) 25
也可以把匿名函數作為返回值返回,
def build(x, y): return lambda: x * x + y * y
5.高階函數
一個函數就可以接收另一個函數作為參數,這種函數就稱之為高階函數。
def add(x, y, f): return f(x) + f(y) def zheng(n): if n > 0: return n else: return -n x = -5 y = 10 f = zheng #變量指向函數,函數名就是變量 print(add(x, y, f))
5.1 map()
和reduce()
函數
map()
函數接收兩個參數,一個是函數,一個是Iterable
,map
將傳入的函數依次作用到序列的每個元素,並把結果作為新的Iterator
返回。
>>> def f(x): ... return x * x ... >>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9]) >>> list(r) [1, 4, 9, 16, 25, 36, 49, 64, 81]
再看reduce
的用法。reduce
把一個函數作用在一個序列[x1, x2, x3, ...]
上,這個函數必須接收兩個參數,reduce
把結果繼續和序列的下一個元素做累積計算:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
>>> from functools import reduce >>> def add(x, y): ... return x + y ... >>> reduce(add, [1, 3, 5, 7, 9]) 25
作業:
利用map
和reduce
編寫一個str2float
函數,把字符串'123.456'
轉換成浮點數123.456
:
摘選了一個答案:
def str2float(str): d = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9} def s2n(s): return d[s] def fn(a, b): return reduce(lambda x, y: x * 10 + y, map(s2n, a)) + reduce(lambda x, y: x * 10 + y, map(s2n, b)) * (10 ** -len(b)) theS = str.split('.') return fn(theS[0], theS[1])
5.2 filter
Python內建的filter()
函數用於過濾序列。
和map()
類似,filter()
也接收一個函數和一個序列。和map()
不同的是,filter()
把傳入的函數依次作用於每個元素,然后根據返回值是True
還是False
決定保留還是丟棄該元素。
def is_odd(n): return n % 2 == 1 list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])) # 結果: [1, 5, 9, 15]
用filter求素數
def _odd_iter(): #構造一個從3
開始的奇數序列,這是一個生成器,並且是一個無限序列。 n = 1 while True: n = n + 2 yield n
def _not_divisible(n): #定義一個篩選函數 return lambda x: x % n > 0
def primes(): #定義一個生成器,不斷返回下一個素數 yield 2 it = _odd_iter() # 初始序列 while True: n = next(it) # 返回序列的第一個數 #每次循環,it會改變,n取新序列的第一個數。 yield n it = filter(_not_divisible(n), it) # 構造新序列 #循環一次,就將n及n倍數的值去掉返回新的Iterator
#將lambda函數寫在filter中,篩選有問題,沒搞懂是怎么回事,應該是閉包的問題。
# 打印1000以內的素數: for n in primes(): if n < 1000: print(n) else: break
5.3 stored
sorted()
函數也是一個高階函數,可以接收一個key
函數來實現自定義的排序,例如按絕對值大小排序:
先將序列輸入key函數,獲取新序列,然后再排序。
>>> sorted([36, 5, -12, 9, -21]) [-21, -12, 5, 9, 36] >>> sorted([36, 5, -12, 9, -21], key=abs) [5, 9, -12, -21, 36] >>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True) #反向排序 ['Zoo', 'Credit', 'bob', 'about']
key指定的函數將作用於list的每一個元素上,並根據key函數返回的結果進行排序。
序列中的元素是一個個傳入key函數,再返回一個新序列。
max,min
salaries={ 'siry':3000, 'tom':7000, 'lili':10000, 'jack':2000 } # 需求1:找出薪資最高的那個人=》lili # res=max([3,200,11,300,399]) # print(res) # res=max(salaries) # print(res) salaries={ 'siry':3000, 'tom':7000, 'lili':10000, 'jack':2000 } # 迭代出的內容 比較的值 # 'siry' 3000 # 'tom' 7000 # 'lili' 10000 # 'jack' 2000 # def func(k): # return salaries[k] # ========================max的應用 # res=max(salaries,key=func) # 返回值=func('siry') # print(res) # res=max(salaries,key=lambda k:salaries[k]) # print(res) # ========================min的應用 # res=min(salaries,key=lambda k:salaries[k]) # print(res)
5.4 函數作為返回值
閉包
https://blog.csdn.net/qq_37616069/article/details/79646905
在函數內部再定義一個函數,並且這個函數用到了外邊函數的變量,那么將這個函數以及用到的一些變量稱之為閉包
函數lazy_sum
中又定義了函數sum
,並且,內部函數sum
可以引用外部函數lazy_sum
的參數和局部變量,當lazy_sum
返回函數sum
時,相關參數和變量都保存在返回的函數中,這種稱為“閉包
#形成閉包的條件
#1、必須要有一個內嵌函數
#2、內嵌函數中要對外層函數變量的引用
#3、外部函數必須返回內嵌函數
返回閉包時牢記一點:返回函數不要引用任何循環變量,或者后續會發生變化的變量。
如果一定要引用循環變量怎么辦?方法是再創建一個函數,用該函數的參數綁定循環變量當前的值,無論該循環變量后續如何更改,已綁定到函數參數的值不變:
def count(): def f(j): def g(): return j*j return g fs = [] for i in range(1, 4): fs.append(f(i)) # f(i)立刻被執行,因此i的當前值被傳入f() return fs
5.5 裝飾器
可以在不修改原有代碼的情況下,為被裝飾的對象增加新的功能或者附加限制條件或者幫助輸出,這種在代碼運行期間動態增加功能的方式,稱之為“裝飾器”(Decorator)
要先理解閉包。
def outer(func): # 裝飾函數 def inner(): print("認證成功!") result = func() print("日志添加成功") return result return inner @outer def f1(): # 被裝飾函數 print("業務部門1數據接口......")
a.程序開始運行,從上到下開始解釋。讀到def outer(func)時,發現這是一個函數的定義,將其函數體放入內存中,然后跳過。
b.跳到@outer時,程序被@這個python語法糖吸引住,知道這是個裝飾器,按規矩要立即執行,於是程序開始運行@后面那個名字outer所定義的函數。
c.程序返回到outer函數,開始執行裝飾器的語法規則。規則是:被裝飾函數的名字會被當作函數形參傳遞給裝飾函數。裝飾函數執行它自己內部的代碼后,會將它的返回值賦值給被裝飾函數。原來f1函數被當作參數傳遞給了func,而f1這個函數名之后會指向inner函數。 注意:@outer和outer()是有區別的。沒有括號時,outer函數會被立即執行。這個與傳統的用括號才能調用函數不同。
d.程序開始執行outer函數內部的內容,一開始它又碰到了一個函數inner,inner函數定義塊被程序觀察到后不會立刻執行,而是讀入內存中(這是默認規則)。
e.再往下,碰到return inner,返回值是個函數名,並且這個函數名會被賦值給f1這個被裝飾函數,也就是f1 = inner。此時,f1函數被新的函數inner覆蓋了(實際上是f1這個函數名更改成指向inner這個函數名指向的函數體內存地址,f1不再指向它原來的函數體的內存地址),再往后調用f1的時候將執行inner函數內的代碼,而不是先前的函數體。那么先前的函數體去哪了?還記得我們將f1當做參數傳遞給func這個形參么?func這個變量保存了老的函數在內存中的地址,通過它就可以執行老的函數體,你能在inner函數里看到result = func()這句代碼,它就是這么干的!
f.接下來,還沒有結束。當業務部門,依然通過f1()的方式調用f1函數時,執行的就不再是舊的f1函數的代碼,而是inner函數的代碼。在本例中,它首先會打印個“認證成功”的提示,很顯然你可以換成任意的代碼,這只是個示例;然后,它會執行func函數並將返回值賦值給變量result,這個func函數就是舊的f1函數;接着,它又打印了“日志添加成功”的提示,這也只是個示例,可以換成任何你想要的;最后返回result這個變量。我們在業務部門的代碼上可以用r = f1()的方式接收result的值。
g.以上流程走完后,你應該看出來了,在沒有對業務部門的代碼和接口調用方式做任何修改的同時,也沒有對基礎平台部原有的代碼做內部修改,僅僅是添加了一個裝飾函數,就實現了我們的需求,在函數調用前進行認證,調用后寫入日志。這就是裝飾器的最大作用。
f1._name_ 輸出inner。Python內置functools.wraps(),可以解決它們的__name__已經從原來的'f1'變成了'inner'的問題
import functools def log(text): ###now = log('execute')(now) def decorator(func): @functools.wraps(func) def wrapper(*args, **kw): print('%s %s():' % (text, func.__name__)) return func(*args, **kw) return wrapper return decorator @log('execute') ###decorator本身需要傳入參數 def now(): print('2015-3-25')
#帶參數 def outer(func): def inner(*args, *kwargs): print("認證成功") result = func(*args, **kwargs) print("日志添加成功") return result return inner @outer def f1(name, age): print("{}正在調用業務部門1的數據接口".format(name)) # 調用方法 f1("jack", 19)
#有多個裝飾器 def outer1(func): def inner(*args,**kwargs): print("認證成功!") result = func(*args,**kwargs) print("日志添加成功") return result return inner def outer2(func): def inner(*args,**kwargs): print("一條歡迎信息。。。") result = func(*args,**kwargs) print("一條歡送信息。。。") return result return inner @outer1 @outer2 def f1(name,age): print("%s 正在連接業務部門1數據接口......"%name) # 調用方法 f1("jack",18)
###裝飾器中形參是函數 # 認證函數 def auth(request,kargs): print("認證成功!") # 日志函數 def log(request,kargs): print("日志添加成功") # 裝飾器函數。接收兩個參數,這兩個參數應該是某個函數的名字。 def Filter(auth_func,log_func): # 第一層封裝,f1函數實際上被傳遞給了main_fuc這個參數 def outer(main_func): # 第二層封裝,auth和log函數的參數值被傳遞到了這里 def wrapper(request,kargs): # 下面代碼的判斷邏輯不重要,重要的是參數的引用和返回值 before_result = auth(request,kargs) if(before_result != None): return before_result; main_result = main_func(request,kargs) if(main_result != None): return main_result; after_result = log(request,kargs) if(after_result != None): return after_result; return wrapper return outer # 注意了,這里的裝飾器函數有參數哦,它的意思是先執行filter函數。然后將filter函數的返回值作為裝飾器函數的名字返回到這里。所以,Filter(auth,log) = outer , @Filter(auth,log) = @outer @Filter(auth,log) def f1(name,age): print("%s 正在連接業務部門1數據接口......"%name) # 調用方法 f1("jack",18)
5.6 偏函數
functools.partial的作用就是,把一個函數的某些參數給固定住(也就是設置默認值),返回一個新的函數。
>>> import functools >>> int2 = functools.partial(int, base=2) >>> int2('1000000') 64 >>> int2('1010101') 85
固定參數,返回新的函數。
參考:菜鳥教程和廖雪峰Python3