函數
簡介
? 函數是組織好的,可重復使用的,用來實現,單一或相關聯的功能
? 函數能提高模塊的利用行,和代碼的重復利用率以及冗余,其實你已經使用了很多的Python函數,比如print()。
? 但是你也可以自己創建函數,這種叫做自定義函數
函數規則
- 函數代碼塊必須以def 關鍵字開頭,后面為函數標識符名以及()
- 任何傳入的參數和自定變量必須放在圓括號中間。圓括號之間可以用來定義參數
- 函數的第一行語句可以選擇性的使用注釋字符來表明函數的作用及說明
- 函數的內容以冒號起始,并且必須縮進
- return [表達式] 結束函數,選擇性的返回一個值給調用該函數方。不帶表達式的函數默認會返回
None
語法
#語法 def functionname( parameters ): "函數_文檔字符串" function_suite return [expression] #實例 def printme( str ): "打印傳入的字符串到標準顯示設備上" print(str) return
默認情況下,參數值和參數名稱是按函數聲明中定義的順序匹配起來的。
函數調用
- 定義一個函數只能給函數一個名稱,指定函數里包含的參數,和代碼塊結構
- 函數基本結構完成以后,你可以通過另一個函數調用執行,也可以直接從python提示符執行
#!/usr/bin/python # -*- coding: UTF-8 -*- # 定義函數 def printme( str ): #打印任何傳入的字符串 print(str); return; # 調用函數 printme("hello world"); #傳參 printme("再次調用同一函數");
參數傳遞
- 函數已經講清楚了,但是函數不夠完美,比如一個例子,函數里定義一個變量a='hello world' 調用函數我們只能返回一個不能變化的字符串,我們如果想打印別的字符串就不行了
例子:
接上面函數調用的代碼
我們告訴printme每次打印我傳進來的東西,這個過程叫做 傳遞參數 , 簡稱
傳參
實參和形參
? 參數還有分別:
? 我們調用函數時傳遞的這個
hello world
被稱為 實際參數 ,因為這個是我們實際交給函數處理的內容,簡稱 實參
? 定義函數時,括號里的str,只是一個變量的名字,被稱為 形式參數 ,因為在函數定義時它只是一個形式,表示這里需要一個參數,檢查 形參? 傳遞多個參數:
? 參數可以傳遞多個,用逗號
,
分開實參角度
- 按照位置傳參
def mymax(x,y): #此時x=10,y=20 the_max = x if x > y else y > return the_max ma = mymax(10,20) print(ma)
? 2.按照關鍵字傳參
def mymax(x,y): #此時x = 20,y = 10 print(x,y) the_max = x if x > y else y return the_max ma = mymax(y = 10,x = 20) print(ma)
? 3.位置、關鍵字形式混著用
def mymax(x,y): #此時x = 10,y = 20 print(x,y) the_max = x if x > y else y return the_max ma = mymax(10,y = 20) print(ma)
? 正確用法:
- 問題一:位置參數必須在關鍵字參數的前面
- 問題二:對于一個形參只能賦值一次
?
形參角度
位置參數必須傳值
def mymax(x,y): #此時x = 10,y = 20 print(x,y) the_max = x if x > y else y return the_max #調用mymax不傳遞參數 ma = mymax() print(ma) #結果 TypeError: mymax() missing 2 required positional arguments: 'x' and 'y'
默認參數
- 正常使用
作用:可以將變化最小的值設置成默認參數
- 默認參數的定義
def stu_info(name,sex = "male"): """打印學生信息函數,由于班中大部分學生都是男生, 所以設置默認參數sex的默認值為'male' """ print(name,sex) stu_info('alex') stu_info('eva','female')
- 參數陷阱:默認參數是一個可變數據類型
def defult_param(a,l = []): l.append(a) print(l) defult_param('alex') defult_param('egon')
動態參數
? 按位置傳值多余的參數都由args統一接收,保存成一個元組的形式
def mysum(*args): the_sum = 0 for i in args: the_sum+=i return the_sum the_sum = mysum(1,2,3,4) print(the_sum)
? 按默認參數傳入多個參數,保存成為字典格式
def stu_info(**kwargs): print(kwargs) print(kwargs['name'],kwargs['sex']) stu_info(name = 'alex',sex = 'male')
命名空間和作用域
- 命名空間的本質:存放名字與值的綁定關系
命名空間一共分為三種: 全局命名空間 內置命名空間 局部命名空間
內置命名空間
存放了python解釋器為我們提供的名字:input print str list tuple 拿過來就可以用的函數方法三種命名空間直接的加載和順序
? 加載順序:內置命名空間(程序運行前加載) --> 全局命名空間(程序運行中:從上到下加載) --> 局部命名空間(程序運行中,調用時才會加載)
取值
? 在局部調用:局部命名空間->全局命名空間->內置命名空間
x = 1 def f(x): print(x) print(10)
? 在全局調用:全局命名空間->內置命名空間
#全局x x = 1 def f(x): print(x) f(10) print(x) #全局max print(max)
- 作用域
作用域就是作用范圍,按照生效范圍可以分為全局作用域和局部作用域。
全局作用域:包含 內置名稱空間、全局名稱空間 ,在整個文件的任意位置都能被引用、全局有效
局部作用域:局部名稱空間,只能在局部范圍 內 生效
globals和locals方法
print(globals()) print(locals()) # 在局部調用globals和locals def func(): a = 12 b = 20 print(locals()) print(globals()) func() # 關鍵字global a = 10 def func(): global a a = 20 print(a) func() print(a)
函數小結
-
定義函數的規則:
1.定義:def 關鍵詞開頭,空格之后接函數名稱和圓括號()。
2.參數:圓括號用來接收參數。若傳入多個參數,參數之間用逗號分割。 參數可以定義多個,也可以不定義?! 涤泻芏喾N,如果涉及到多種參數的定義,應始終遵循位置參數、*args、默認參數、**kwargs順序定義?! ∪缟鲜龆x過程中某參數類型缺省,其他參數依舊遵循上述排序
3.注釋:函數的第一行語句應該添加注釋。
4.函數體:函數內容以冒號起始,并且縮進。
5.返回值:return [表達式] 結束函數。不帶表達式的return相當于返回 Nonedef 函數名(參數1,參數2,*args,默認參數,**kwargs):
"""注釋:函數功能和參數說明"""
函數體
……
return 返回值 -
調用函數的規則:
1.函數名()
函數名后面+圓括號就是函數的調用。
2.參數:
圓括號用來接收參數。
若傳入多個參數:
應按先位置傳值,再按關鍵字傳值
具體的傳入順序應按照函數定義的參數情況而定
3.返回值
如果函數有返回值,還應該定義“變量”接收返回值
如果返回值有多個,也可以用多個變量來接收,變量數應和返回值數目一致無返回值的情況:
函數名()有返回值的情況:
變量 = 函數名()多個變量接收多返回值:
變量1,變量2,... = 函數名() -
命名空間:
一共有三種命名空間從大范圍到小范圍的順序:內置命名空間、全局命名空間、局部命名空間
作用域(包括函數的作用域鏈): 小范圍的可以用大范圍的但是大范圍的不能用小范圍的范圍從大到小
高階函數
-
函數的本質
函數名的本質實際上就是函數的內存地址
- 可以被引用
def func():
print('in func')
f = func
f()
print(f)
- 可以被當作容器的元素
def f1():
print('f1')
def f2():
print('f2')
l = [f1,f2]
d = {'f1':f1,'f2':f2}
l[0]()
d['f1']()
-
閉包函數
def func(): name = 'eva' def inner(): print(name)
閉包函數定義 :內部函數包含對外部作用域而非全局作用域名字的引用,該內部函數稱為閉包函數
函數內部定義的函數稱為內部函數,這樣就不能拿到函數內部的變量和函數,只能返回
#輸出的__closure__有cell元素 :是閉包函數 def func(): name = 'eva' def inner(): print(name) print(inner.__closure__) return inner f = func() f() #輸出的__closure__為None :不是閉包函數 name = 'egon' def func2(): def inner(): print(name) print(inner.__closure__) return inner f2 = func2() f2()
裝飾器
python裝飾器是利用函數特性的閉包完成的,
開放封閉原則 :面向對象編程核心原則,軟件實體應該是可以擴展的,而不可修改的。也就是說對擴展時開放的,而對代碼本體修改是封閉的
- 開放原則:對擴展時開放的
- 封閉原則:對修改時封閉的
? 裝飾器的作用 :裝飾器是可以在不改動原有代碼功能的基礎上,可以擴展的功能。正是對開放封閉原則的完美體現。
? 工作原理 :依然在調用原有功能代碼,但是能實現調用裝飾器函數里的功能
def index(): '''這是一個主頁信息''' print('from index') print(index.__doc__) #查看函數注釋的方法 print(index.__name__) #查看函數名的方法 print(inner.__closure__) #輸出的__closure__有cell元素 :是閉包函數
? 標準格式 :
from functools import wraps #已經開發好的裝飾器模塊 def deco(func): # 裝飾器函數 #@wraps(func) # 加在最內層函數正上方 def wrapper(*args,**kwargs): # 加一堆參數 *args,**kwargs接受 return func(*args,**kwargs) return wrapper #返回內部函數的名字(注意) @deco #==> index = deco(index) #語法糖 @裝飾器函數名,下面必須是被裝飾的函數 def index(): '''哈哈哈哈''' print('from index') print(index.__doc__) print(index.__name__)
固定格式 :
使用情況 :
? 在已經寫好的發版的程序功能基礎上,需要對一個函數執行前后增加功能的時候
? 有的時候也會寫好一些裝飾器,裝在需要裝飾的函數上
生成器
我們知道的迭代器有兩種:一種是調用方法直接返回的,一種是可迭代對象通過執行iter方法得到的,迭代器有的好處是可以節省內存。
如果在某些情況下,我們也需要節省內存,就只能自己寫。我們自己寫的這個能實現迭代器功能的東西就叫生成器。
Python中提供的生成器:
1.生成器函數:常規函數定義,但是,使用yield語句而不是return語句返回結果。yield語句一次返回一個結果,在每個結果中間,掛起函數的狀態,以便下次重它離開的地方繼續執行
2.生成器表達式:類似于列表推導,但是,生成器返回按需產生結果的一個對象,而不是一次構建一個結果列表
生成器Generator:
本質:迭代器(所以自帶了__iter__方法和__next__方法,不需要我們去實現)
特點:惰性運算,開發者自定義
生成器函數:
一個包含yield關鍵字的函數就是一個生成器函數。yield可以為我們從函數中返回值,但是yield又不同于return,return的執行意味著程序的結束,調用生成器函數不會得到返回的具體的值,而是得到一個可迭代的對象。每一次獲取這個可迭代對象的值,就能推動函數的執行,獲取新的返回值。直到函數執行結束。
示例代碼:
import time
def genrator_fun1():
a = 1
print('現在定義了a變量')
yield a
b = 2
print('現在又定義了b變量')
yield b
g1 = genrator_fun1()
print('g1 : ',g1) #打印g1可以發現g1就是一個生成器
print('-'*20) #我是華麗的分割線
print(next(g1))
time.sleep(1) #sleep一秒看清執行過程
print(next(g1))
生成器有什么好處呢?就是不會一下子在內存中生成太多數據
文件監聽例子
import time
def tail(filename):
f = open(filename)
f.seek(0, 2) #從文件末尾算起
while True:
line = f.readline() # 讀取文件中新的文本行
if not line:
time.sleep(0.1)
continue
yield line
tail_g = tail('tmp')
for line in tail_g:
print(line)
列表推導式和生成器表達式
#老男孩由于峰哥的強勢加盟很快走上了上市之路,alex思來想去決定下幾個雞蛋來報答峰哥
egg_list=['雞蛋%s' %i for i in range(10)] #列表解析
#峰哥瞅著alex下的一筐雞蛋,捂住了鼻子,說了句:哥,你還是給我只母雞吧,我自己回家下
laomuji=('雞蛋%s' %i for i in range(10))#生成器表達式
print(laomuji)
print(next(laomuji)) #next本質就是調用__next__
print(laomuji.__next__())
print(next(laomuji))
總結:
1.把列表解析的[]換成()得到的就是生成器表達式
2.列表解析與生成器表達式都是一種便利的編程方式,只不過生成器表達式更節省內存
3.Python不但使用迭代器協議,讓for循環變得更加通用。大部分內置函數,也是使用迭代器協議訪問對象的。例如, sum函數是Python的內置函數,該函數使用迭代器協議訪問對象,而生成器實現了迭代器協議,所以,我們可以直接這樣計算一系列值的和:
sum(x ** 2 for x in range(4))
而不用多此一舉的先構造一個列表:
sum([x ** 2 for x in range(4)])
更多詳情請看:https://www.cnblogs.com/Eva-J/articles/7276796.html
匿名函數
匿名函數
匿名函數:為了解決那些功能很簡單的需求而設計的一句話函數
#這段代碼
def calc(n):
return n**n
print(calc(10))
#換成匿名函數
calc = lambda n:n**n
print(calc(10))
上面是我們對calc這個匿名函數的分析,下面給出了一個關于匿名函數格式的說明
函數名 = lambda 參數 :返回值
#參數可以有多個,用逗號隔開
#匿名函數不管邏輯多復雜,只能寫一行,且邏輯執行結束后的內容就是返回值
#返回值和正常的函數一樣可以是任意數據類型
我們可以看出,匿名函數并不是真的不能有名字。
匿名函數的調用和正常的調用也沒有什么分別。 就是 函數名(參數) 就可以了~~~
練一練:
請把以下函數變成匿名函數
def add(x,y):
return x+y
上面是匿名函數的函數用法。除此之外,匿名函數也不是浪得虛名,它真的可以匿名。在和其他功能函數合作的時候
l=[3,2,100,999,213,1111,31121,333]
print(max(l))
dic={'k1':10,'k2':100,'k3':30}
print(max(dic))
print(dic[max(dic,key=lambda k:dic[k])])
res = map(lambda x:x**2,[1,5,7,4,8])
for i in res:
print(i)
輸出
1
25
49
16
64
res = filter(lambda x:x>10,[5,8,11,9,15])
for i in res:
print(i)
輸出
11
15
面試題練一練
現有兩個元組(('a'),('b')),(('c'),('d')),請使用python中匿名函數生成列表[{'a':'c'},{'b':'d'}]
#答案一
test = lambda t1,t2 :[{i:j} for i,j in zip(t1,t2)]
print(test(t1,t2))
#答案二
print(list(map(lambda t:{t[0]:t[1]},zip(t1,t2))))
#還可以這樣寫
print([{i:j} for i,j in zip(t1,t2)])
1.下面程序的輸出結果是:
d = lambda p:p*2
t = lambda p:p*3
x = 2
x = d(x)
x = t(x)
x = d(x)
print x
2.現有兩元組(('a'),('b')),(('c'),('d')),請使用python中匿名函數生成列表[{'a':'c'},{'b':'d'}]
3.以下代碼的輸出是什么?請給出答案并解釋。
def multipliers():
return [lambda x:i*x for i in range(4)]
print([m(2) for m in multipliers()])
請修改multipliers的定義來產生期望的結果。
遞歸函數
Python內部:最大的遞歸深度為1000次
內置函數
# dir可以查看一個數據可以調用哪些方法 也可以通過某一個方法是不是在結果中,從而判斷
dir()
callable()
print()
sep # 分隔符
ent # 結束符
file # 寫入文件,文件句柄
eval()
exec()
open()
input()
id()
sum min max # 都支持接受iter 可迭代對象
ord # 字符找到Ascii碼的位置
chr # Ascii碼的位置找到字符
repr # 打印某個變量的值的值得時候,更便于區分類型
reversed # 反轉 本身不變,返回的是個迭代器
filter # 返回值為迭代器 查找
map # 返回值為迭代器
enumerate #枚舉函數 enumerate(iterable,1)
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
