第四篇: python函数续
1、函數的嵌套
函數的嵌套調用:在調用一個函數的過程中,又調用了其它函數
示例1:
def bar():print('from nbar')def foo():print('from foo')bar() foo() #執行結果為 C:\Users\Administrator\AppData\Local\Programs\Python\Python36\python.exe C:/Users/Administrator/PycharmProjects/python18/day4/函數嵌套.py from foo from nbar?
示例2:求函數所帶四個參數的最大值
def max2(x,y):if x > y:return xelse:return ydef max4(a,b,c,d):res1=max2(a,b)res2=max2(res1,c)res3=max2(res2,d)return res3 print(max(1,2,3,-1))#執行結果如下 C:\Users\Administrator\AppData\Local\Programs\Python\Python36\python.exe C:/Users/Administrator/PycharmProjects/python18/day4/函數嵌套.py 3?
函數的嵌套定義:在一個函數的內部,又定義另外一個函數
2、名稱空間和作用域
名稱空間:存放名字的地方,準確的說名稱空間是存放名字與變量值綁定關系的地方名稱空間共有三種名稱空間既:
1、內置名稱空間:在python解釋器啟動時產生,存放一些python內置的名字
2、全局名稱空間:在執行文件時產生,存放文件級別定義的名字
3、局部名稱空間:在執行文件的過程中,如果調用了函數,則會產生該函數的局部名稱空間,用來存放該函數內定義的名字,該名字在函數調用時生效,在函數調用結束后失效
加載順序:內置===》全局===》局部
優先掌握一:名字的查找順序是:局部==》全局===》內置
作用域:作用的范圍
全局作用域:全局存貨,全局有效:globals()
局部作用域:臨時存活,局部生效:locals() x=11111111111111111111111111111111111111111111# def f1(): # x=1 # y=2 # def f2():pass # # print(locals()) # print(globals()) # # f1() # print(locals() is globals()) # print(locals()) # # print(dir(globals()['__builtins__']))#global nonlocal掌握 # x=1 # def f1(): # global x # x=2 # # f1() # print(x)# l=[] # def f2(): # l.append('f2') # # f2() # print(l)# x=0 # def f1(): # # x=1 # def f2(): # # x=2 # def f3(): # # global x # nonlocal x # x=3 # f3() # # print(x) # f2() # print(x) # f1() # print(x)
優先掌握二:作用域關系,在函數定義時就已經固定,于調用位置無關,在調用函數時,必須必須必須回到函數原來定義的位置去找作用域關系
x=1 def f1():def f2():print(x)return f2# func=f1() # print(func) # x=10000000 # func() # x=10000000def foo(func):x=300000000func() #f2() x=10000000000000000000000foo(f1()) # x=10000000000000000000000 # foo(f1())
3、閉包函數
1. 定義在函數內部的函數
2. 包含對外部作用域名字的引用,而不是對全局作用域名字的引用,那么該內部函數就稱為閉包函數
?
閉包函數的應用:惰性計算
import requests #pip3 install requests# def get(url): # return requests.get(url).text # # print(get('https://www.python.org')) # print(get('https://www.python.org')) # print(get('https://www.python.org')) # print(get('https://www.python.org'))# def index(url): # # url='https://www.python.org' # def get(): # # return requests.get(url).text # print(requests.get(url).text) # # return get # # python_web=index('https://www.python.org') # baidu_web=index('https://www.baidu.com')# python_web() # baidu_web()name='egon' def index(url):x=1y=2def wrapper():# x# y# return requests.get(url).textprint(name)return wrapperpython_web=index('https://www.python.org')# print(python_web.__closure__[0].cell_contents) print(python_web.__closure__) # print(python_web.__closure__[0].cell_contents) # print(python_web.__closure__[1].cell_contents) # print(python_web.__closure__[2].cell_contents)?
4、裝飾器
1、 開放封閉原則:對擴展是開放的,對修改是封閉
2、 裝飾器:裝飾它人的工具,裝飾器本身可以是任意可調用對象,被裝飾的對象本身也可以是任意可調用對象
2.1 裝飾器的遵循的原則:
2.1.1、 不修改被裝飾對象的源代碼
2.1.2、 不修改被調用對象的調用方式
裝飾器名,必須寫在被裝飾對象的正上方,并且是單獨一行
import timedef timmer(func):# func=indexdef wrapper():start=time.time()func()stop=time.time()print('run time is %s' %(stop-start))return wrapper@timmer # index=timmer(index) def index():time.sleep(3)print('welcome to index') @timmer # home=timmer(home) def home():time.sleep(2)print('welcome to home page')index() home()裝飾器補充
#補充一:wraps# import time # from functools import wraps # # def timmer(func): # @wraps(func) # def wrapper(*args,**kwargs): # start=time.time() # res=func(*args,**kwargs) # stop=time.time() # print('run time is %s' %(stop-start)) # return res # return wrapper # # # @timmer # index=timmer(index) # def index(): # '''這是index函數''' # time.sleep(3) # print('welcome to index') # return 123 # # print(index.__doc__) # # print(help(index))#補充二:一個函數頭頂上可以多個裝飾器 import time from functools import wraps current_user={'user':None}def timmer(func):@wraps(func)def wrapper(*args,**kwargs):start=time.time()res=func(*args,**kwargs)stop=time.time()print('run time is %s' %(stop-start))return resreturn wrapper def auth(auth_type='file'):def deco(func):def wrapper(*args, **kwargs):if auth_type == 'file':if current_user['user']:return func(*args, **kwargs)name = input('name: ').strip()password = input('password: ').strip()with open('db.txt', encoding='utf-8') as f:user_dic = eval(f.read())if name in user_dic and password == user_dic[name]:res = func(*args, **kwargs)current_user['user'] = namereturn reselse:print('user or password error')elif auth_type == 'mysql':print('mysql')elif auth_type == 'ldap':print('ldap')else:print('not valid auth_type')return wrapperreturn deco@timmer #index=timmer(wrapper) @auth() # @deco #index=deco(index) #wrapper def index():'''這是index函數'''time.sleep(3)print('welcome to index')return 123# print(index.__doc__) # print(help(index))index()
5、生成器
?學習生成器之前,我們先來看看什么是列表生成式
#列表生成式 b = [ i*2 for i in range(10)] print(b)###########打印輸出########### #[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]通過列表生成式,我們可以直接創建一個列表。但是,受到內存限制,列表容量肯定是有限的。而且,創建一個包含100萬個元素的列表,不僅占用很大的存儲空間,還需要花費很長時間,如果我們僅僅需要訪問前面幾個元素,那后面絕大多數元素占用的空間都白白浪費了。所以,如果列表元素可以按照某種算法推算出來,那我們是否可以在循環的過程中不斷推算出后續的元素呢?這樣就不必創建完整的list,從而節省大量的空間。在Python中,這種調用時才會生成相應數據的機制,稱為生成器:generator
要創建一個generator,有很多種方法,第一種方法很簡單,只要把一個列表生成式的[]改成(),就創建了一個生成器
#生成器 l = [ i*2 for i in range(10)] print(l)g = (i*2 for i in range(10)) print(g)###########打印輸出########### #[0, 2, 4, 6, 8, 10, 12, 14, 16, 18] #<generator object <genexpr> at 0x0064AAE0>print(g) 打印出來的信息顯示g是一個生成器,創建l和g的區別僅在于最外層的[]和(),l是一個list,而g是一個generator;我們可以直接打印出list的每一個元素,但我們怎么打印出generator的每一個元素呢?如果要一個一個打印出來,可以通過next()函數獲得generator的下一個返回值
#生成器next打印 print(next(g)) #......... 不斷next 打印10次 #.......... print(next(g))###########打印輸出########### #0 #........ #18 #Traceback (most recent call last): # File "<stdin>", line 1, in <module> #StopIteration我們講過,generator保存的是算法,每次調用next(g),就計算出g的下一個元素的值,直到計算到最后一個元素,沒有更多的元素時,拋出StopIteration的錯誤。
上面這種不斷調用next(g)實在是太變態了,正確的方法是使用for循環,因為generator也是可迭代對象,所以,我們創建了一個generator后,基本上永遠不會調用next(),而是通過for循環來迭代它,并且不需要關心StopIteration的錯誤
#生成器for調用 g = (i*2 for i in range(10)) #不用擔心出現StopIteration錯誤 for i in g:print(i)###########打印輸出########### # 0 # 2 # 4 # 6 # 8 # 10 # 12 # 14 # 16 # 18generator非常強大。如果推算的算法比較復雜,用列表生成式轉換的生成器無法去實現時,我們還可以用函數來實現。比如,著名的斐波拉契數列(Fibonacci)
#函數表示斐波拉契數列 def fib(max):n, a, b = 0, 0, 1while n < max:print(b)a, b = b, a + bn += 1return 'done'fib(5) ###########打印輸出########### # 1 # 1 # 2 # 3 # 5仔細觀察,可以看出,fib函數實際上是定義了斐波那契數列的推算規則,可以從第一個元素開始,推算出后續任意的元素,這種邏輯其實非常類似generator;也就是說,上面的函數和generator僅一步之遙,那我們能不能把上面的函數變成一個生成器呢?
#斐波拉契數列轉換為generator def fib(max):n, a, b = 0, 0, 1while n < max:#print(b)yield ba, b = b, a + bn += 1return 'done'print(type(fib(5))) #打印fib(5)的類型 for i in fib(5): #for循環去調用print(i) ###########打印輸出########### # <class 'generator'> # 1 # 1 # 2 # 3 # 5要把fib函數變成generator,只需要把print(b)改為yield b就可以了,這就是定義generator的另一種方法。如果一個函數定義中包含yield關鍵字,那么這個函數就不再是一個普通函數,而是一個generator
但是用for循環調用generator時,會發現拿不到generator的return語句的返回值,也就是return的值沒有打印出來,現在我們來看看怎么去打印generator的返回值
#獲取generator的返回值 def fib(max):n, a, b = 0, 0, 1while n < max:#print(b)yield ba, b = b, a + bn += 1return 'done'g = fib(5) while True:try:x = next(g)print( x)except StopIteration as e:print(e.value)break ###########打印輸出########### # 1 # 1 # 2 # 3 # 5 # done如果想要拿到返回值,必須捕獲StopIteration錯誤,返回值包含在StopIteration的value中,關于如何捕獲錯誤,后面的錯誤處理還會詳細講解。
還可通過yield實現在單線程的情況下實現并發運算的效果
import time def consumer(name):print("%s 準備吃包子啦!" %name)while True:baozi = yieldprint("包子[%s]來了,被[%s]吃了!" %(baozi,name))def producer(name):c = consumer('A')c2 = consumer('B')c.__next__() #c.__next__()等同于next(c)c2.__next__()print("老子開始準備做包子啦!")for i in range(10):time.sleep(1)print("%s做了2個包子!"%(name))c.send(i)c2.send(i)producer("lzl")
6、迭代器
迭代:是一個重復的過程,每一次重復,都是基于上一次的結果而來
# while True: #單純的重復 # print('你瞅啥')# l=['a','b','c','d'] # count=0 # while count < len(l): # print(l[count]) # count+=1dic={'name':'egon','sex':'m',"age":18} #上述按照索引的取值方式,不適于沒有索引的數據類型迭代器:
可迭代對象iterable:凡是對象下有__iter__方法:對象.__iter__,該對象就是可迭代對象
迭代器對象:可迭代對象執行內置的__iter__方法,得到的結果就是迭代器對象
# dic={'name':'egon','sex':'m',"age":18} # # i=dic.__iter__() # # print(i) #iterator迭代器 # # # i.__next__() #next(i) # print(next(i)) # print(next(i)) # print(next(i)) # print(next(i)) #StopIteration # # l=['a','b','c','d'] # # i=l.__iter__() # print(next(i)) # print(next(i)) # print(next(i)) # print(next(i)) # print(next(i)) #StopIteration不依賴于索引的取值方式
# l=['a','b','c','d'] # dic={'name':'egon','sex':'m',"age":18} # iter_l=iter(l) # iter_dic=iter(dic) # while True: # try: # # print(next(iter_l)) # k=next(iter_dic) # print(k,dic[k]) # except StopIteration: # break什么是迭代器對象:
1 有__iter__,執行得到仍然是迭代本身
2 有__next__
迭代器對象的優點
1:提供了一種統一的(不依賴于索引的)迭代方式
2:迭代器本身,比起其他數據類型更省內存
迭代器對象的缺點
1:一次性,只能往后走,不能回退,不如索引取值靈活
2:無法預知什么時候取值結束,即無法預知長度
?
轉載于:https://www.cnblogs.com/junxun/p/7224615.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的第四篇: python函数续的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Cuckoo WIndows 安装
- 下一篇: websocket python爬虫_p