一 爲什麼要用函數之不用函數的問題html
#一、代碼的組織結構不清晰,可讀性差 #二、遇到重複的功能只能重複編寫實現代碼,代碼冗餘 #三、功能須要擴展時,須要找出全部實現該功能的地方修改之,沒法統一管理且維護難度極大
二 函數是什麼python
針對二中的問題,想象生活中的例子,修理工須要實現準備好工具箱裏面放好錘子,扳手,鉗子等工具,而後遇到錘釘子的場景,拿來錘子用就能夠,而無需臨時再製造一把錘子。
修理工===>程序員
具有某一功能的工具===>函數
要想使用工具,須要事先準備好,而後拿來就用且能夠重複使用
要想用函數,須要先定義,再使用
三 函數分類linux
#一、內置函數 爲了方便咱們的開發,針對一些簡單的功能,python解釋器已經爲咱們定義好了的函數即內置函數。對於內置函數,咱們能夠拿來就用而無需事先定義,如len(),sum(),max() ps:咱們將會在最後詳細介紹經常使用的內置函數。 #二、自定義函數 很明顯內置函數所能提供的功能是有限的,這就須要咱們本身根據需求,事先定製好咱們本身的函數來實現某種功能,之後,在遇到應用場景時,調用自定義的函數便可。例如
1 如何自定義函數?git
#語法 def 函數名(參數1,參數2,參數3,...): '''註釋''' 函數體 return 返回的值 #函數名要能反映其意義
def auth(user:str,password:str)->int: ''' auth function :param user: 用戶名 :param password: 密碼 :return: 認證結果 ''' if user == 'egon' and password == '123': return 1 # print(auth.__annotations__) #{'user': <class 'str'>, 'password': <class 'str'>, 'return': <class 'int'>} user=input('用戶名>>: ').strip() pwd=input('密碼>>: ').strip() res=auth(user,pwd) print(res)
2 函數使用的原則:先定義,再調用程序員
函數即「變量」,「變量」必須先定義後引用。未定義而直接引用函數,就至關於在引用一個不存在的變量名
#測試一 def foo(): print('from foo') bar() foo() #報錯 #測試二 def bar(): print('from bar') def foo(): print('from foo') bar() foo() #正常 #測試三 def foo(): print('from foo') bar() def bar(): print('from bar') foo() #會報錯嗎? #結論:函數的使用,必須遵循原則:先定義,後調用 #咱們在使用函數時,必定要明確地區分定義階段和調用階段 #定義階段 def foo(): print('from foo') bar() def bar(): print('from bar') #調用階段 foo()
五 函數在定義階段都幹了哪些事?算法
#只檢測語法,不執行代碼 也就說,語法錯誤在函數定義階段就會檢測出來,而代碼的邏輯錯誤只有在執行時纔會知道
六 定義函數的三種形式sql
#一、無參:應用場景僅僅只是執行一些操做,好比與用戶交互,打印 #二、有參:須要根據外部傳進來的參數,才能執行相應的邏輯,好比統計長度,求最大值最小值 #三、空函數:設計代碼結構
#定義階段 def tell_tag(tag,n): #有參數 print(tag*n) def tell_msg(): #無參數 print('hello world') #調用階段 tell_tag('*',12) tell_msg() tell_tag('*',12) ''' ************ hello world ************ ''' #結論: #一、定義時無參,意味着調用時也無需傳入參數 #二、定義時有參,意味着調用時則必須傳入參數
def auth(user,password): ''' auth function :param user: 用戶名 :param password: 密碼 :return: 認證結果 ''' pass def get(filename): ''' :param filename: :return: ''' pass def put(filename): ''' :param filename: :return: ''' def ls(dirname): ''' :param dirname: :return: ''' pass #程序的體系結構立見
1 調用函數express
函數的調用:函數名加括號
1 先找到名字 2 根據名字調用代碼
2 函數返回值編程
無return->None
return 1個值->返回1個值 return 逗號分隔多個值->元組
何時該有返回值?
調用函數,通過一系列的操做,最後要拿到一個明確的結果,則必需要有返回值
一般有參函數須要有返回值,輸入參數,通過計算,獲得一個最終的結果
何時不須要有返回值?
調用函數,僅僅只是執行一系列的操做,最後不須要獲得什麼結果,則無需有返回值
一般無參函數不須要有返回值
3 函數調用的三種形式數據結構
1 語句形式:foo()
2 表達式形式:3*len('hello') 3 當中另一個函數的參數:range(len('hello'))
1 形參與實參
#形參即變量名,實參即變量值,函數調用時,將值綁定到變量名上,函數調用結束,解除綁定
2 具體應用
#一、位置參數:按照從左到右的順序定義的參數 位置形參:必選參數 位置實參:按照位置給形參傳值 #二、關鍵字參數:按照key=value的形式定義的實參 無需按照位置爲形參傳值 注意的問題: 1. 關鍵字實參必須在位置實參右面 2. 對同一個形參不能重複傳值 #三、默認參數:形參在定義時就已經爲其賦值 能夠傳值也能夠不傳值,常常須要變得參數定義成位置形參,變化較小的參數定義成默認參數(形參) 注意的問題: 1. 只在定義時賦值一次 2. 默認參數的定義應該在位置形參右面 3. 默認參數一般應該定義成不可變類型 #四、可變長參數: 可變長指的是實參值的個數不固定 而實參有按位置和按關鍵字兩種形式定義,針對這兩種形式的可變長,形參對應有兩種解決方案來完整地存放它們,分別是*args,**kwargs ===========*args=========== def foo(x,y,*args): print(x,y) print(args) foo(1,2,3,4,5) def foo(x,y,*args): print(x,y) print(args) foo(1,2,*[3,4,5]) def foo(x,y,z): print(x,y,z) foo(*[1,2,3]) ===========**kwargs=========== def foo(x,y,**kwargs): print(x,y) print(kwargs) foo(1,y=2,a=1,b=2,c=3) def foo(x,y,**kwargs): print(x,y) print(kwargs) foo(1,y=2,**{'a':1,'b':2,'c':3}) def foo(x,y,z): print(x,y,z) foo(**{'z':1,'x':2,'y':3}) ===========*args+**kwargs=========== def foo(x,y): print(x,y) def wrapper(*args,**kwargs): print('====>') foo(*args,**kwargs) #五、命名關鍵字參數:*後定義的參數,必須被傳值(有默認值的除外),且必須按照關鍵字實參的形式傳遞 能夠保證,傳入的參數中必定包含某些關鍵字 def foo(x,y,*args,a=1,b,**kwargs): print(x,y) print(args) print(a) print(b) print(kwargs) foo(1,2,3,4,5,b=3,c=4,d=5) 結果: 1 2 (3, 4, 5) 1 3 {'c': 4, 'd': 5}
1 函數是第一類對象,即函數能夠看成數據傳遞
#1 能夠被引用 #2 能夠看成參數傳遞 #3 返回值能夠是函數 #3 能夠看成容器類型的元素
2 利用該特性,優雅的取代多分支的if
def foo(): print('foo') def bar(): print('bar') dic={ 'foo':foo, 'bar':bar, } while True: choice=input('>>: ').strip() if choice in dic: dic[choice]()
1 函數的嵌套調用
def max(x,y): return x if x > y else y def max4(a,b,c,d): res1=max(a,b) res2=max(res1,c) res3=max(res2,d) return res3 print(max4(1,2,3,4))
2 函數的嵌套定義
def f1(): def f2(): def f3(): print('from f3') f3() f2() f1() f3() #報錯,爲什麼?請看下一小節
十 什麼是名稱空間?
#名稱空間:存放名字的地方,三種名稱空間,(以前遺留的問題x=1,1存放於內存中,那名字x存放在哪裏呢?名稱空間正是存放名字x與1綁定關係的地方)
十一 名稱空間的加載順序
python test.py
#一、python解釋器先啓動,於是首先加載的是:內置名稱空間 #二、執行test.py文件,而後以文件爲基礎,加載全局名稱空間 #三、在執行文件的過程當中若是調用函數,則臨時產生局部名稱空間
十二 名字的查找順序
局部名稱空間--->全局名稱空間--->內置名稱空間
#須要注意的是:在全局沒法查看局部的,在局部能夠查看全局的,以下示例 # max=1 def f1(): # max=2 def f2(): # max=3 print(max) f2() f1() print(max)
十三 做用域
#一、做用域即範圍 - 全局範圍(內置名稱空間與全局名稱空間屬於該範圍):全局存活,全局有效 - 局部範圍(局部名稱空間屬於該範圍):臨時存活,局部有效 #二、做用域關係是在函數定義階段就已經固定的,與函數的調用位置無關,以下 x=1 def f1(): def f2(): print(x) return f2 x=100 def f3(func): x=2 func() x=10000 f3(f1()) #三、查看做用域:globals(),locals() LEGB 表明名字查找順序: locals -> enclosing function -> globals -> __builtins__ locals 是函數內的名字空間,包括局部變量和形參 enclosing 外部嵌套函數的名字空間(閉包中常見) globals 全局變量,函數定義所在模塊的名字空間 builtins 內置模塊的名字空間
global與nonlocal關鍵字
1 什麼是閉包?
#內部函數包含對外部做用域而非全局做用域的引用 #提示:以前咱們都是經過參數將外部的值傳給函數,閉包提供了另一種思路,包起來嘍,包起呦,包起來哇 def counter(): n=0 def incr(): nonlocal n x=n n+=1 return x return incr c=counter() print(c()) print(c()) print(c()) print(c.__closure__[0].cell_contents) #查看閉包的元素
2 閉包的意義與應用
#閉包的意義:返回的函數對象,不只僅是一個函數對象,在該函數外還包裹了一層做用域,這使得,該函數不管在何處調用,優先使用本身外層包裹的做用域 #應用領域:延遲計算(原來咱們是傳參,如今咱們是包起來) from urllib.request import urlopen def index(url): def get(): return urlopen(url).read() return get baidu=index('http://www.baidu.com') print(baidu().decode('utf-8'))
裝飾器就是閉包函數的一種應用場景
1 爲什麼要用裝飾器
#開放封閉原則:對修改封閉,對擴展開放
2 什麼是裝飾器
裝飾器他人的器具,自己能夠是任意可調用對象,被裝飾者也能夠是任意可調用對象。
強調裝飾器的原則:1 不修改被裝飾對象的源代碼 2 不修改被裝飾對象的調用方式 裝飾器的目標:在遵循1和2的前提下,爲被裝飾對象添加上新功能
3 裝飾器的使用
import time def timmer(func): def wrapper(*args,**kwargs): start_time=time.time() res=func(*args,**kwargs) stop_time=time.time() print('run time is %s' %(stop_time-start_time)) return res return wrapper @timmer def foo(): time.sleep(3) print('from foo') foo()
def auth(driver='file'): def auth2(func): def wrapper(*args,**kwargs): name=input("user: ") pwd=input("pwd: ") if driver == 'file': if name == 'egon' and pwd == '123': print('login successful') res=func(*args,**kwargs) return res elif driver == 'ldap': print('ldap') return wrapper return auth2 @auth(driver='file') def foo(name): print(name) foo('egon')
4 裝飾器語法
被裝飾函數的正上方,單獨一行
@deco1
@deco2
@deco3
def foo(): pass foo=deco1(deco2(deco3(foo)))
5 裝飾器補充:wraps
from functools import wraps def deco(func): @wraps(func) #加在最內層函數正上方 def wrapper(*args,**kwargs): return func(*args,**kwargs) return wrapper @deco def index(): '''哈哈哈哈''' print('from index') print(index.__doc__)
十六 迭代的概念
#迭代器即迭代的工具,那什麼是迭代呢?
#迭代是一個重複的過程,每次重複即一次迭代,而且每次迭代的結果都是下一次迭代的初始值 while True: #只是單純地重複,於是不是迭代 print('===>') l=[1,2,3] count=0 while count < len(l): #迭代 print(l[count]) count+=1
十七 爲什麼要有迭代器?什麼是可迭代對象?什麼是迭代器對象?
def auth(driver='file'): def auth2(func): def wrapper(*args,**kwargs): name=input("user: ") pwd=input("pwd: ") if driver == 'file': if name == 'egon' and pwd == '123': print('login successful') res=func(*args,**kwargs) return res elif driver == 'ldap': print('ldap') return wrapper return auth2 @auth(driver='file') def foo(name): print(name) foo('egon')
十八 迭代器對象的使用
dic={'a':1,'b':2,'c':3} iter_dic=dic.__iter__() #獲得迭代器對象,迭代器對象即有__iter__又有__next__,可是:迭代器.__iter__()獲得的仍然是迭代器自己 iter_dic.__iter__() is iter_dic #True print(iter_dic.__next__()) #等同於next(iter_dic) print(iter_dic.__next__()) #等同於next(iter_dic) print(iter_dic.__next__()) #等同於next(iter_dic) # print(iter_dic.__next__()) #拋出異常StopIteration,或者說結束標誌 #有了迭代器,咱們就能夠不依賴索引迭代取值了 iter_dic=dic.__iter__() while 1: try: k=next(iter_dic) print(dic[k]) except StopIteration: break #這麼寫太醜陋了,須要咱們本身捕捉異常,控制next,python這麼牛逼,能不能幫我解決呢?能,請看for循環
十九 for循環
#基於for循環,咱們能夠徹底再也不依賴索引去取值了 dic={'a':1,'b':2,'c':3} for k in dic: print(dic[k]) #for循環的工做原理 #1:執行in後對象的dic.__iter__()方法,獲得一個迭代器對象iter_dic #2: 執行next(iter_dic),將獲得的值賦值給k,而後執行循環體代碼 #3: 重複過程2,直到捕捉到異常StopIteration,結束循環
二十 迭代器的優缺點
#優勢: - 提供一種統一的、不依賴於索引的迭代方式 - 惰性計算,節省內存 #缺點: - 沒法獲取長度(只有在next完畢才知道到底有幾個值) - 一次性的,只能日後走,不能往前退
二一 什麼是生成器
#只要函數內部包含有yield關鍵字,那麼函數名()的到的結果就是生成器,而且不會執行函數內部代碼 def func(): print('====>first') yield 1 print('====>second') yield 2 print('====>third') yield 3 print('====>end') g=func() print(g) #<generator object func at 0x0000000002184360>
二二 生成器就是迭代器
g.__iter__ g.__next__ #二、因此生成器就是迭代器,所以能夠這麼取值 res=next(g) print(res)
二三 協程函數
#yield關鍵字的另一種使用形式:表達式形式的yield def eater(name): print('%s 準備開始吃飯啦' %name) food_list=[] while True: food=yield food_list print('%s 吃了 %s' % (name,food)) food_list.append(food) g=eater('egon') g.send(None) #對於表達式形式的yield,在使用時,第一次必須傳None,g.send(None)等同於next(g) g.send('蒸羊羔') g.send('蒸鹿茸') g.send('蒸熊掌') g.send('燒素鴨') g.close() g.send('燒素鵝') g.send('燒鹿尾')
二四 yield總結
#一、把函數作成迭代器 #二、對比return,能夠返回屢次值,能夠掛起/保存函數的運行狀態
#一、首先強調:面向過程編程絕對不是用函數編程這麼簡單,面向過程是一種編程思路、思想,而編程思路是不依賴於具體的語言或語法的。言外之意是即便咱們不依賴於函數,也能夠基於面向過程的思想編寫程序 #二、定義 面向過程的核心是過程二字,過程指的是解決問題的步驟,即先幹什麼再幹什麼 基於面向過程設計程序就比如在設計一條流水線,是一種機械式的思惟方式 #三、優勢:複雜的問題流程化,進而簡單化 #四、缺點:可擴展性差,修改流水線的任意一個階段,都會牽一髮而動全身 #五、應用:擴展性要求不高的場景,典型案例如linux內核,git,httpd #六、舉例 流水線1: 用戶輸入用戶名、密碼--->用戶驗證--->歡迎界面 流水線2: 用戶輸入sql--->sql解析--->執行功能
ps:函數的參數傳入,是函數吃進去的食物,而函數return的返回值,是函數拉出來的結果,面向過程的思路就是,把程序的執行當作一串首尾相連的功能,該功能能夠是函數的形式,而後一個函數吃,拉出的東西給另一個函數吃,另一個函數吃了再繼續拉給下一個函數吃。。。
二六 三元表達式
name=input('姓名>>: ') res='SB' if name == 'alex' else 'NB' print(res)
二七 列表推導式
#一、示例 egg_list=[] for i in range(10): egg_list.append('雞蛋%s' %i) egg_list=['雞蛋%s' %i for i in range(10)] #二、語法 [expression for item1 in iterable1 if condition1 for item2 in iterable2 if condition2 ... for itemN in iterableN if conditionN ] 相似於 res=[] for item1 in iterable1: if condition1: for item2 in iterable2: if condition2 ... for itemN in iterableN: if conditionN: res.append(expression) #三、優勢:方便,改變了編程習慣,可稱之爲聲明式編程
二八 生成器表達式
#一、把列表推導式的[]換成()就是生成器表達式 #二、示例:生一筐雞蛋變成給你一隻老母雞,用的時候就下蛋,這也是生成器的特性 >>> chicken=('雞蛋%s' %i for i in range(5)) >>> chicken <generator object <genexpr> at 0x10143f200> >>> next(chicken) '雞蛋0' >>> list(chicken) #因chicken可迭代,於是能夠轉成列表 ['雞蛋1', '雞蛋2', '雞蛋3', '雞蛋4',] #三、優勢:省內存,一次只產生一個值在內存中
二九 聲明式編程練習題
一、將names=['egon','alex_sb','wupeiqi','yuanhao']中的名字所有變大寫
二、將names=['egon','alex_sb','wupeiqi','yuanhao']中以sb結尾的名字過濾掉,而後保存剩下的名字長度
三、求文件a.txt中最長的行的長度(長度按字符個數算,須要使用max函數)
四、求文件a.txt中總共包含的字符個數?思考爲什麼在第一次以後的n次sum求和獲得的結果爲0?(須要使用sum函數)
五、思考題
with open('a.txt') as f: g=(len(line) for line in f) print(sum(g)) #爲什麼報錯?
六、文件shopping.txt內容以下
mac,20000,3
lenovo,3000,10 tesla,1000000,10 chicken,200,1
求總共花了多少錢?
打印出全部商品的信息,格式爲[{'name':'xxx','price':333,'count':3},...]
求單價大於10000的商品信息,格式同上
#題目一 names=['egon','alex_sb','wupeiqi','yuanhao'] names=[name.upper() for name in names] #題目二 names=['egon','alex_sb','wupeiqi','yuanhao'] names=[len(name) for name in names if not name.endswith('sb')] #題目三 with open('a.txt',encoding='utf-8') as f: print(max(len(line) for line in f)) #題目四 with open('a.txt', encoding='utf-8') as f: print(sum(len(line) for line in f)) print(sum(len(line) for line in f)) #求包換換行符在內的文件全部的字符數,爲什麼獲得的值爲0? print(sum(len(line) for line in f)) #求包換換行符在內的文件全部的字符數,爲什麼獲得的值爲0? #題目五(略) #題目六:每次必須從新打開文件或seek到文件開頭,由於迭代完一次就結束了 with open('a.txt',encoding='utf-8') as f: info=[line.split() for line in f] cost=sum(float(unit_price)*int(count) for _,unit_price,count in info) print(cost) with open('a.txt',encoding='utf-8') as f: info=[{ 'name': line.split()[0], 'price': float(line.split()[1]), 'count': int(line.split()[2]), } for line in f] print(info) with open('a.txt',encoding='utf-8') as f: info=[{ 'name': line.split()[0], 'price': float(line.split()[1]), 'count': int(line.split()[2]), } for line in f if float(line.split()[1]) > 10000] print(info)
三十 遞歸調用的定義
#遞歸調用是函數嵌套調用的一種特殊形式,函數在調用時,直接或間接調用了自身,就是遞歸調用
三一 遞歸分爲兩個階段:遞推,回溯
#圖解。。。 # salary(5)=salary(4)+300 # salary(4)=salary(3)+300 # salary(3)=salary(2)+300 # salary(2)=salary(1)+300 # salary(1)=100 # # salary(n)=salary(n-1)+300 n>1 # salary(1) =100 n=1 def salary(n): if n == 1: return 100 return salary(n-1)+300 print(salary(5))
三二 python中的遞歸效率低且沒有尾遞歸優化
#python中的遞歸 python中的遞歸效率低,須要在進入下一次遞歸時保留當前的狀態,在其餘語言中能夠有解決方法:尾遞歸優化,即在函數的最後一步(而非最後一行)調用本身,尾遞歸優化:http://egon09.blog.51cto.com/9161406/1842475 可是python又沒有尾遞歸,且對遞歸層級作了限制 #總結遞歸的使用: 1. 必須有一個明確的結束條件 2. 每次進入更深一層遞歸時,問題規模相比上次遞歸都應有所減小 3. 遞歸效率不高,遞歸層次過多會致使棧溢出(在計算機中,函數調用是經過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。因爲棧的大小不是無限的,因此,遞歸調用的次數過多,會致使棧溢出)
三三 能夠修改遞歸最大深度
import sys sys.getrecursionlimit() sys.setrecursionlimit(2000) n=1 def test(): global n print(n) n+=1 test() test() 雖然能夠設置,可是由於不是尾遞歸,仍然要保存棧,內存大小必定,不可能無限遞歸
三四 二分法
想從一個按照從小到大排列的數字列表中找到指定的數字,遍歷的效率過低,用二分法(算法的一種,算法是解決問題的方法)能夠極大低縮小問題規模
一 三元表達式
name=input('姓名>>: ') res='SB' if name == 'alex' else 'NB' print(res)
二 列表推導式
#一、示例 egg_list=[] for i in range(10): egg_list.append('雞蛋%s' %i) egg_list=['雞蛋%s' %i for i in range(10)] #二、語法 [expression for item1 in iterable1 if condition1 for item2 in iterable2 if condition2 ... for itemN in iterableN if conditionN ] 相似於 res=[] for item1 in iterable1: if condition1: for item2 in iterable2: if condition2 ... for itemN in iterableN: if conditionN: res.append(expression) #三、優勢:方便,改變了編程習慣,可稱之爲聲明式編程
三 生成器表達式
#一、把列表推導式的[]換成()就是生成器表達式 #二、示例:生一筐雞蛋變成給你一隻老母雞,用的時候就下蛋,這也是生成器的特性 >>> chicken=('雞蛋%s' %i for i in range(5)) >>> chicken <generator object <genexpr> at 0x10143f200> >>> next(chicken) '雞蛋0' >>> list(chicken) #因chicken可迭代,於是能夠轉成列表 ['雞蛋1', '雞蛋2', '雞蛋3', '雞蛋4',] #三、優勢:省內存,一次只產生一個值在內存中
四 聲明式編程練習題
一、將names=['egon','alex_sb','wupeiqi','yuanhao']中的名字所有變大寫
二、將names=['egon','alex_sb','wupeiqi','yuanhao']中以sb結尾的名字過濾掉,而後保存剩下的名字長度
三、求文件a.txt中最長的行的長度(長度按字符個數算,須要使用max函數)
四、求文件a.txt中總共包含的字符個數?思考爲什麼在第一次以後的n次sum求和獲得的結果爲0?(須要使用sum函數)
五、思考題
with open('a.txt') as f: g=(len(line) for line in f) print(sum(g)) #爲什麼報錯?
六、文件shopping.txt內容以下
mac,20000,3
lenovo,3000,10 tesla,1000000,10 chicken,200,1
求總共花了多少錢?
打印出全部商品的信息,格式爲[{'name':'xxx','price':333,'count':3},...]
求單價大於10000的商品信息,格式同上
#題目一 names=['egon','alex_sb','wupeiqi','yuanhao'] names=[name.upper() for name in names] #題目二 names=['egon','alex_sb','wupeiqi','yuanhao'] names=[len(name) for name in names if not name.endswith('sb')] #題目三 with open('a.txt',encoding='utf-8') as f: print(max(len(line) for line in f)) #題目四 with open('a.txt', encoding='utf-8') as f: print(sum(len(line) for line in f)) print(sum(len(line) for line in f)) #求包換換行符在內的文件全部的字符數,爲什麼獲得的值爲0? print(sum(len(line) for line in f)) #求包換換行符在內的文件全部的字符數,爲什麼獲得的值爲0? #題目五(略) #題目六:每次必須從新打開文件或seek到文件開頭,由於迭代完一次就結束了 with open('a.txt',encoding='utf-8') as f: info=[line.split() for line in f] cost=sum(float(unit_price)*int(count) for _,unit_price,count in info) print(cost) with open('a.txt',encoding='utf-8') as f: info=[{ 'name': line.split()[0], 'price': float(line.split()[1]), 'count': int(line.split()[2]), } for line in f] print(info) with open('a.txt',encoding='utf-8') as f: info=[{ 'name': line.split()[0], 'price': float(line.split()[1]), 'count': int(line.split()[2]), } for line in f if float(line.split()[1]) > 10000] print(info)
一 遞歸調用的定義
#遞歸調用是函數嵌套調用的一種特殊形式,函數在調用時,直接或間接調用了自身,就是遞歸調用
二 遞歸分爲兩個階段:遞推,回溯
#圖解。。。 # salary(5)=salary(4)+300 # salary(4)=salary(3)+300 # salary(3)=salary(2)+300 # salary(2)=salary(1)+300 # salary(1)=100 # # salary(n)=salary(n-1)+300 n>1 # salary(1) =100 n=1 def salary(n): if n == 1: return 100 return salary(n-1)+300 print(salary(5))
三 python中的遞歸效率低且沒有尾遞歸優化
#python中的遞歸 python中的遞歸效率低,須要在進入下一次遞歸時保留當前的狀態,在其餘語言中能夠有解決方法:尾遞歸優化,即在函數的最後一步(而非最後一行)調用本身,尾遞歸優化:http://egon09.blog.51cto.com/9161406/1842475 可是python又沒有尾遞歸,且對遞歸層級作了限制 #總結遞歸的使用: 1. 必須有一個明確的結束條件 2. 每次進入更深一層遞歸時,問題規模相比上次遞歸都應有所減小 3. 遞歸效率不高,遞歸層次過多會致使棧溢出(在計算機中,函數調用是經過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。因爲棧的大小不是無限的,因此,遞歸調用的次數過多,會致使棧溢出)
四 能夠修改遞歸最大深度
import sys sys.getrecursionlimit() sys.setrecursionlimit(2000) n=1 def test(): global n print(n) n+=1 test() test() 雖然能夠設置,可是由於不是尾遞歸,仍然要保存棧,內存大小必定,不可能無限遞歸
五 二分法
想從一個按照從小到大排列的數字列表中找到指定的數字,遍歷的效率過低,用二分法(算法的一種,算法是解決問題的方法)能夠極大低縮小問題規模
l=[1,2,10,30,33,99,101,200,301,402] #從小到大排列的數字列表 def search(num,l): print(l) if len(l) > 0: mid=len(l)//2 if num > l[mid]: #in the right l=l[mid+1:] elif num < l[mid]: #in the left l=l[:mid] else: print('find it') return search(num,l) else: #若是值不存在,則列表切爲空 print('not exists') return search(100,l)
l=[1,2,10,30,33,99,101,200,301,402]
def search(num,l,start=0,stop=len(l)-1): if start <= stop: mid=start+(stop-start)//2 print('start:[%s] stop:[%s] mid:[%s] mid_val:[%s]' %(start,stop,mid,l[mid])) if num > l[mid]: start=mid+1 elif num < l[mid]: stop=mid-1 else: print('find it',mid) return search(num,l,start,stop) else: #若是stop > start則意味着列表實際上已經所有切完,即切爲空 print('not exists') return search(301,l)
1 什麼是匿名函數?
匿名就是沒有名字
def func(x,y,z=1): return x+y+z 匿名 lambda x,y,z=1:x+y+z #與函數有相同的做用域,可是匿名意味着引用計數爲0,使用一次就釋放,除非讓其有名字 func=lambda x,y,z=1:x+y+z func(1,2,3) #讓其有名字就沒有意義
2 有名字的函數與匿名函數的對比
#有名函數與匿名函數的對比 有名函數:循環使用,保存了名字,經過名字就能夠重複引用函數功能 匿名函數:一次性使用,隨時隨時定義 應用:max,min,sorted,map,reduce,filter
#注意:內置函數id()能夠返回一個對象的身份,返回值爲整數。這個整數一般對應與該對象在內存中的位置,但這與python的具體實現有關,不該該做爲對身份的定義,即不夠精準,最精準的仍是之內存地址爲準。is運算符用於比較兩個對象的身份,等號比較兩個對象的值,內置函數type()則返回一個對象的類型 #更多內置函數:https://docs.python.org/3/library/functions.html?highlight=built#ascii
#字符串能夠提供的參數 's' None >>> format('some string','s') 'some string' >>> format('some string') 'some string' #整形數值能夠提供的參數有 'b' 'c' 'd' 'o' 'x' 'X' 'n' None >>> format(3,'b') #轉換成二進制 '11' >>> format(97,'c') #轉換unicode成字符 'a' >>> format(11,'d') #轉換成10進制 '11' >>> format(11,'o') #轉換成8進制 '13' >>> format(11,'x') #轉換成16進制 小寫字母表示 'b' >>> format(11,'X') #轉換成16進制 大寫字母表示 'B' >>> format(11,'n') #和d同樣 '11' >>> format(11) #默認和d同樣 '11' #浮點數能夠提供的參數有 'e' 'E' 'f' 'F' 'g' 'G' 'n' '%' None >>> format(314159267,'e') #科學計數法,默認保留6位小數 '3.141593e+08' >>> format(314159267,'0.2e') #科學計數法,指定保留2位小數 '3.14e+08' >>> format(314159267,'0.2E') #科學計數法,指定保留2位小數,採用大寫E表示 '3.14E+08' >>> format(314159267,'f') #小數點計數法,默認保留6位小數 '314159267.000000' >>> format(3.14159267000,'f') #小數點計數法,默認保留6位小數 '3.141593' >>> format(3.14159267000,'0.8f') #小數點計數法,指定保留8位小數 '3.14159267' >>> format(3.14159267000,'0.10f') #小數點計數法,指定保留10位小數 '3.1415926700' >>> format(3.14e+1000000,'F') #小數點計數法,無窮大轉換成大小字母 'INF' #g的格式化比較特殊,假設p爲格式中指定的保留小數位數,先嚐試採用科學計數法格式化,獲得冪指數exp,若是-4<=exp<p,則採用小數計數法,並保留p-1-exp位小數,不然按小數計數法計數,並按p-1保留小數位數 >>> format(0.00003141566,'.1g') #p=1,exp=-5 ==》 -4<=exp<p不成立,按科學計數法計數,保留0位小數點 '3e-05' >>> format(0.00003141566,'.2g') #p=1,exp=-5 ==》 -4<=exp<p不成立,按科學計數法計數,保留1位小數點 '3.1e-05' >>> format(0.00003141566,'.3g') #p=1,exp=-5 ==》 -4<=exp<p不成立,按科學計數法計數,保留2位小數點 '3.14e-05' >>> format(0.00003141566,'.3G') #p=1,exp=-5 ==》 -4<=exp<p不成立,按科學計數法計數,保留0位小數點,E使用大寫 '3.14E-05' >>> format(3.1415926777,'.1g') #p=1,exp=0 ==》 -4<=exp<p成立,按小數計數法計數,保留0位小數點 '3' >>> format(3.1415926777,'.2g') #p=1,exp=0 ==》 -4<=exp<p成立,按小數計數法計數,保留1位小數點 '3.1' >>> format(3.1415926777,'.3g') #p=1,exp=0 ==》 -4<=exp<p成立,按小數計數法計數,保留2位小數點 '3.14' >>> format(0.00003141566,'.1n') #和g相同 '3e-05' >>> format(0.00003141566,'.3n') #和g相同 '3.14e-05' >>> format(0.00003141566) #和g相同 '3.141566e-05'
字典的運算:最小值,最大值,排序
salaries={ 'egon':3000, 'alex':100000000, 'wupeiqi':10000, 'yuanhao':2000 } 迭代字典,取得是key,於是比較的是key的最大和最小值 >>> max(salaries) 'yuanhao' >>> min(salaries) 'alex' 能夠取values,來比較 >>> max(salaries.values()) >>> min(salaries.values()) 但一般咱們都是想取出,工資最高的那我的名,即比較的是salaries的值,獲得的是鍵 >>> max(salaries,key=lambda k:salary[k]) 'alex' >>> min(salaries,key=lambda k:salary[k]) 'yuanhao' 也能夠經過zip的方式實現 salaries_and_names=zip(salaries.values(),salaries.keys()) 先比較值,值相同則比較鍵 >>> max(salaries_and_names) (100000000, 'alex') salaries_and_names是迭代器,於是只能訪問一次 >>> min(salaries_and_names) Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: min() arg is an empty sequence sorted(iterable,key=None,reverse=False)
#一、語法 # eval(str,[,globasl[,locals]]) # exec(str,[,globasl[,locals]]) #二、區別 #示例一: s='1+2+3' print(eval(s)) #eval用來執行表達式,並返回表達式執行的結果 print(exec(s)) #exec用來執行語句,不會返回任何值 ''' 6 None ''' #示例二: print(eval('1+2+x',{'x':3},{'x':30})) #返回33 print(exec('1+2+x',{'x':3},{'x':30})) #返回None # print(eval('for i in range(10):print(i)')) #語法錯誤,eval不能執行表達式 print(exec('for i in range(10):print(i)'))
compile(str,filename,kind)
filename:用於追蹤str來自於哪一個文件,若是不想追蹤就能夠不定義
kind能夠是:single表明一條語句,exec表明一組語句,eval表明一個表達式
s='for i in range(10):print(i)' code=compile(s,'','exec') exec(code) s='1+2+3' code=compile(s,'','eval') eval(code)