python之函數的介紹

 

文件處理

1.介紹:html

計算機系統分爲:計算機硬件,操做系統,應用程序三部分。python

咱們用python或其餘語言編寫的應用程序若想要把數據永久保存下來,必需要保存於硬盤中,這就涉及到應用程序要操做硬件,衆所周知,應用程序是沒法直接操做硬件的,這就用到了操做系統。操做系統把複雜的硬件操做封裝成簡單的接口給用戶/應用程序使用,其中文件就是操做系統提供給應用程序來操做硬盤虛擬概念,用戶或應用程序經過操做文件,能夠將本身的數據永久保存下來。linux

有了文件的概念,咱們無需再去考慮操做硬盤的細節,只須要關注操做文件的流程:算法

1. 打開文件,獲得文件句柄並賦值給一個變量 (打開文件時,須要指定文件路徑和以何等方式打開文件,打開後,便可獲取該文件句柄,往後經過此文件句柄對該文件操做。)
2. 經過句柄對文件進行操做
3. 關閉文件
#1. 打開文件,獲得文件句柄並賦值給一個變量
f=open('a.txt','r',encoding='utf-8') #默認打開模式就爲r

#2. 經過句柄對文件進行操做
data=f.read()

#3. 關閉文件
f.close()

 

2.打開文件的模式文件句柄 = open('文件路徑', '模式')編程

1. 打開文件的模式有(默認爲文本模式):vim

  • r ,只讀模式【默認模式,文件必須存在,不存在則拋出異常】
  • w,只寫模式【不可讀;不存在則建立;存在則清空內容;】
  • x, 只寫模式【不可讀;不存在則建立,存在則報錯】
  • a, 追加模式【可讀;   不存在則建立;存在則只追加內容;】

2. "b"表示以字節的方式操做 ,對於非文本文件,咱們只能使用b模式。windows

  • rb  或 r+b
  • wb 或 w+b
  • xb 或 w+b
  • ab 或 a+b

全部文件也都是以字節的形式存儲的,使用這種模式無需考慮文本文件的字符編碼、圖片文件的jgp格式、視頻文件的avi格式,以b方式打開時,讀取到的內容是字節類型,寫入時也須要提供字節類型,不能指定編碼 緩存

3. "+" 表示能夠同時讀寫某個文件ruby

  • r+, 讀寫【可讀,可寫】
  • w+,寫讀【可讀,可寫】
  • x+ ,寫讀【可讀,可寫】
  • a+, 寫讀【可讀,可寫】
  • x,只寫模式【不可讀;不存在則建立,存在則報錯】
  • x+ 【可讀,可寫】

4.注意點:數據結構

#強調第一點:
打開一個文件包含兩部分資源:操做系統級打開的文件+應用程序的變量。在操做完畢一個文件時,必須把與該文件的這兩部分資源一個不落地回收,回收方法爲:
一、f.close() #回收操做系統級打開的文件
二、del f #回收應用程序級的變量

其中del f必定要發生在f.close()以後,不然就會致使操做系統打開的文件尚未關閉,白白佔用資源,
而python自動的垃圾回收機制決定了咱們無需考慮del f,這就要求咱們,在操做完畢文件後,必定要記住f.close()

雖然我這麼說,可是不少同窗仍是會很不要臉地忘記f.close(),對於這些不長腦子的同窗,咱們推薦傻瓜式操做方式:使用with關鍵字來幫咱們管理上下文
with open('a.txt','w') as f:
    pass
 
with open('a.txt','r') as read_f,open('b.txt','w') as write_f:
    data=read_f.read()
    write_f.write(data)
資源回收
f=open(...)是由操做系統打開文件,那麼若是咱們沒有爲open指定編碼,那麼打開文件的默認編碼很明顯是操做系統說了算了,操做系統會用本身的默認編碼去打開文件,在windows下是gbk,在linux下是utf-8。
這就用到了上節課講的字符編碼的知識:若要保證不亂碼,文件以什麼方式存的,就要以什麼方式打開。

f=open('a.txt','r',encoding='utf-8')
字符編碼

三、文件的操做方法

f.read() #讀取全部內容,光標移動到文件末尾
f.readline() #讀取一行內容,光標移動到第二行首部
f.readlines() #讀取每一行內容,存放於列表中

f.write('1111\n222\n') #針對文本模式的寫,須要本身寫換行符
f.write('1111\n222\n'.encode('utf-8')) #針對b模式的寫,須要本身寫換行符
f.writelines(['333\n','444\n']) #文件模式
f.writelines([bytes('333\n',encoding='utf-8'),'444\n'.encode('utf-8')]) #b模式

#瞭解
f.readable() #文件是否可讀
f.writable() #文件是否可讀
f.closed #文件是否關閉
f.encoding #若是文件打開模式爲b,則沒有該屬性
f.flush() #馬上將文件內容從內存刷到硬盤
f.name

4.文件的修改

方式一:將硬盤存放的該文件的內容所有加載到內存,在內存中是能夠修改的,修改完畢後,再由內存覆蓋到硬盤(word,vim,nodpad++等編輯器)

import os

with open('a.txt') as read_f,open('.a.txt.swap','w') as write_f:
    data=read_f.read() #所有讀入內存,若是文件很大,會很卡
    data=data.replace('alex','SB') #在內存中完成修改

    write_f.write(data) #一次性寫入新文件

os.remove('a.txt')
os.rename('.a.txt.swap','a.txt')
View Code

方式二:將硬盤存放的該文件的內容一行一行地讀入內存,修改完畢就寫入新文件,最後用新文件覆蓋源文件

import os

with open('a.txt') as read_f,open('.a.txt.swap','w') as write_f:
    for line in read_f:
        line=line.replace('alex','SB')
        write_f.write(line)

os.remove('a.txt')
os.rename('.a.txt.swap','a.txt')
View Code

 

 

函數的介紹

函數的定義:是組織好的,可重複使用的,用來實現單一,或相關聯功能的代碼段。函數的特性:避免代碼重用,提升代碼的可讀性,可維護性。

 def  函數名(參數1,參數2)

     ''' 函數註釋'''

    print('函數體')

    return 返回值

 定義:def關鍵字開頭,空格以後接函數名和圓括號,最後還要加一個冒號,def是固定的,不能變。

 函數名:函數名是包含字母,數字,下劃線的任意組合,可是不能以數字開頭。雖然函數名能夠隨便取名,可是通常儘可能定義成能夠表示函數功能的。

def function_name(a,b):  #function_name 爲函數名, a b 爲參數
print("statement")  #函數體
return something # return不是必須的
View Code

 函數的調用:返回值=函數名(參數1,參數2)#記得函數名後面加括號,原則先定義再調用。

1 #定義階段
2 def foo():
3     print('from foo')
4     bar()
5 def bar():
6     print('from bar')
7 #調用階段
8 foo()
View Code

 

函數參數

行參與實參:

#形參即變量名,實參即變量值,函數調用時,將值綁定到變量名上,函數調用結束,解除綁定。

 

一、位置參數:按照從左到右的順序定義的參數
位置形參:必選參數
位置實參:需按照位置給形參傳值

二、關鍵字參數:按照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)
View Code

五、命名關鍵字參數:*後定義的參數,必須被傳值(有默認值的除外),且必須按照關鍵字實參的形式傳遞
能夠保證,傳入的參數中必定包含某些關鍵字

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)
結果:
2
(3, 4, 5)
3
{'c': 4, 'd': 5}
View Code

 

 小結:

 

 

 函數的返回值

1.return的做用:結束一個函數的執行

def func4():
     print (1111111)
     return#結束一個函數的執行
     print (1242451)
func4()  打印結果爲 1111111
View Code

2.首先返回值能夠是任意的數據類型。

3.函數能夠有返回值:若是有返回值,必需要用變量接收纔有效果。也能夠沒有返回值,沒有返回值的時候分三種狀況:

    1.當不寫return的時候,函數的返回值爲None

    2.當只寫一個return的時候,函數的返回值爲None

    3.return None的時候,函數的返回值爲None(幾乎不用)

4.return返回一個值(一個變量)

5.return返回多個值(多個變量):多個值之間用逗號隔開,以元組的形式返回。接收:能夠用一個變量接收,也能夠用多個變量接收,返回幾個就用幾個變量去接收

def func():
    a = 111
    b = [1, 2, 3]
    c = {'a': 15, 'b': 6}
    return a #返回一個值  打印結果爲:111
    return a,b,c#返回多個值,變量之間按逗號隔開,以元組的形式返回,打印結果:(111, [1, 2, 3], {'a': 15, 'b': 6})

print(func())
View Code 

6 函數沒有返回值的函數

1.不寫return時返回None
def  func():
    a=111
    b=[1,2,3]
ret=func()
print(ret)#打印結果:None


2.只寫一個return時返回None
def  func():
    a=111
    b=[1,2,3]
    return
ret=func()
print(ret)   #打印結果:None

3.return None的時候返回None
def  func():
    a=111
    b=[1,2,3]
    return  None
ret=func()
print(ret)   #打印結果:None
View Code

 

小結:

 

 

全局變量與局部變量

1.定義:在函數內部的變量擁有一個局部做用域,定義在函數外的擁有全局做用域。

局部變量只能在其被聲明的函數內部訪問,而全局變量能夠在整個程序範圍內訪問。調用函數時,全部在函數內聲明的變量名稱都將被加入到做用域中。

##若是函數的內容無global關鍵字,優先讀取局部變量,能讀取區部變量,沒法對全局變量從新賦值,但對於可變類型,能夠對內部元素進行操做。若是函數中有global關鍵字,變量本質就是全局的那個變量,可讀取可複製

gloabal、nonlocal

首先咱們寫這樣一個代碼, 首先在全局聲明一個變量, 而後再局部調用這個變量, 並改變這 個變量的值 

a = 100
def func():   
    global a    # 加了個global表示不再局部建立這個變量了. 而是直接使用全局的a   
    a = 28   
print(a)
func()
print(a)
View Code

global表示. 再也不使用局部做用域中的內容了. 而改用全局做用域中的變量

lst = ["麻花藤", "劉嘉玲", "詹姆斯"]
def func():   
    lst.append("⻢雲")   
    # 對於可變數據類型能夠直接進⾏訪問. 可是不能改地址. 說⽩了. 不能賦值 在函數中賦值就是在局部空間建立了一個變量   
   print(lst)
func()
print(lst)
View Code

nonlocal 表示在局部做用域中, 調用父級命名空間中的變量.

若是父級命名空間中沒有這個變量名,就繼續向上查找.最多找到最外成的函數就結束了

a = 10
def func1():   
    a = 20   
    def func2():
        nonlocal a       
        a = 30       
        print(a)  
    func2()   
    print(a)
func1()
 
結果:
加了nonlocal
30
30
 
不加nonlocal
30
20   
View Code

若是嵌套了不少層, 會是一種什麼效果:?

a = 1
def fun_1():   
    a = 2   
    def fun_2():       
        nonlocal a       
        a = 3       
        def fun_3():           
            a = 4           
            print(a)       
        print(a)       
        fun_3()       
        print(a)   
    print(a)   
    fun_2()   
    print(a)
print(a)
fun_1()
print(a)
View Code

 

 

函數的嵌套

 1定義:在一個函數中定義了另一個函數即一個函數裏用def語句來建立其它的函數的狀況

def outer():


  def inner():


    print('inner')


  print('outer')


  inner()


outer()


inner()    # 此句會出錯
View Code

內部函數不能被外部直接使用,會拋NameError異常

2個體會列子

 

 

匿名函數與三元運算 

1、匿名函數:也叫lambda表達式

1.匿名函數的核心:一些簡單的須要用函數去解決的問題,匿名函數的函數體只有一行

2.參數能夠有多個,用逗號隔開

3.返回值和正常的函數同樣能夠是任意的數據類型

語法:

請把下面的函數轉換成匿名函數
def  add(x,y)
        return x+y
add()

結果:
sum1=lambda x,y:x+y
print(sum1(5,8))
View Code

 與map的應用:

l=[1,2,3,4]
# def func(x):
#     return x*x
# print(list(map(func,l)))

print(list(map(lambda x:x*x,l)))
View Code

filter函數的小應用:

l=[15,24,31,14]
 # def func(x):
 #         return x>20
 # print(list(filter(func,l)))
 
 print(list(filter(lambda x:x>20,l)))
View Code

2、三元運算

python的三元運算格式:

result=值1 if x<y else 值2    這個是什麼意思呢,就是結果=值1 if 條件1 else 值2

>>> def f(x,y):                             
    return x - y if x>y else abs(x-y)  
#若是x大於y就返回x-y的值 ,不然就返回x-y的絕對值

>>> f(3,4)      #3<4,不知足if 條件,它返回else裏面的絕度值
>>> f(4,3)
>>> def f(x,y):
    return 1 if x>y else -1   
#若是x大於y就返回x-y的值 ,不然就返-1
>>> f(3,4)     #3小於4 , 返回-1
-1
>>> f(4,3)      #4大於3,返回1
>>>
View Code

 

函數的遞歸

定義:在函數內部,能夠調用其餘函數。若是一個函數在內部調用自身自己,這個函數就是遞歸函數。優勢是定義簡單,邏輯清晰。理論上,全部的遞歸函數均可以寫成循環的方式,但循環的邏輯不如遞歸清晰。

def fact(n):
    if n==1:
        return 1
    return n * fact(n - 1)
fact(5)  #打印結果爲 120
#計算過程:
===> fact(5)
===> 5 * fact(4)
===> 5 * (4 * fact(3))
===> 5 * (4 * (3 * fact(2)))
===> 5 * (4 * (3 * (2 * fact(1))))
===> 5 * (4 * (3 * (2 * 1)))
===> 5 * (4 * (3 * 2))
===> 5 * (4 * 6)
===> 5 * 24
===> 120
計算階乘

 

遞歸次數的限制:最大遞歸層數作了一個限制:997,可是能夠經過導入sys模塊的方式來修改改變限制的次數(sys模塊:全部和python相關的設置和方法)列如:

import sys
sys.setrecursionlimit(1500)#修改遞歸層數
def recursion(n):
    print(n)
    recursion(n+1)

recursion(1)
#打印結果 1 2 3 .....1497
修改遞歸的次數

 

遞歸的特性:必須有一個明確的結束條件(return),要不就變成死循環了,每次進入更深一層遞歸時,問題規模相比上一次遞歸都應有所減小,遞歸的執行效率不高,遞歸層次過多會致使棧的溢出。列子:

def calc(n):
    v=int(n/2)
    print(v)
    if v==0:
        return
    calc(v)
    
calc(10)
#打印結果 5 2 1 0

*******體會********

def calc(n):
    v=int(n/2)
    if v==0:
        return
    calc(v)
    print(v)
calc(10)
#打印結果 1 2 5 
10除以2

 

遞歸的做用:求斐波那契數列,漢諾塔,多級評論樹,二分查找,求階乘等

# N! = 1 * 2 * 3 * ... * N
def fact(n):
    if n == 1:
        return 1
    return n * fact(n-1)
 fact(n)
利用遞歸函數計算階乘
def move(n, a, b, c):
    if n == 1:
        print('move', a, '-->', c)
    else:
        move(n-1, a, c, b)
        move(1, a, b, c)
        move(n-1, b, a, c)

move(4, 'A', 'B', 'C')
利用遞歸函數移動漢諾塔

二分查找:

從這個列表中找到55的位置l = 【2,3,5,10,15,16,18,22,26,30,32,35,41,42,43,55,56,66,67,69,72,76,82,83,88】

 

這就是二分查找,從上面的列表中能夠觀察到,這個列表是從小到大依次遞增的有序列表。

按照上面的圖就能夠實現查找了

l = [2, 3, 5, 10, 15, 16, 18, 22, 26, 30, 32, 35, 41, 42, 43, 55, 56, 66, 67, 69, 72, 76, 82, 83, 88]
def find(l,aim):
    mid=len(l)//2#取中間值,//長度取整(取出來的是索引)
    if l[mid]>aim:#判斷中間值和要找的那個值的大小關係
        new_l=l[:mid]#顧頭不顧尾
        return find(new_l,aim)#遞歸算法中在每次函數調用的時候在前面加return
    elif l[mid]<aim:
        new_l=l[mid+1:]
        return find(new_l,aim)
    else:
        return l[mid]
print(find(l,66))
簡單的二分法
l = [2, 3, 5, 10, 15, 16, 18, 22, 26, 30, 32, 35, 41, 42, 43, 55, 56, 66, 67, 69, 72, 76, 82, 83, 88]
def func(l, aim,start = 0,end = len(l)-1):
    mid = (start+end)//2#求中間的數
    if not l[start:end+1]:#若是你要找的數不在裏面,就return'你查找的數字不在這個列表裏面'
        return  '你查找的數字不在這個列表裏面'
    elif aim > l[mid]:
        return func(l,aim,mid+1,end)
    elif aim < l[mid]:
        return func(l,aim,start,mid-1)
    elif aim == l[mid]:
        print("bingo")
        return mid

index = func(l,55)
print(index)
# print(func(l,41))
升級版二分法

 

尾遞歸優化:使用遞歸函數須要注意防止棧溢出。在計算機中,函數調用是經過棧(stack)(#至關於存放數據的盒子)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。因爲棧的大小不是無限的,因此,遞歸調用的次數過多,會致使棧溢出。解決遞歸調用棧溢出的方法是經過尾遞歸優化。在函數返回的時候,調用自身自己,而且,return語句不能包含表達式。這樣,編譯器或者解釋器就能夠把尾遞歸作優化,使遞歸自己不管調用多少次,都只佔用一個棧幀,不會出現棧溢出的狀況

 

 列子:

def cal(n):
    print(n)
    return cal(n+1)
cal(n)
#不是全部的語言都支持,在python裏不支持尾遞歸優化
View Code

 

小結

使用遞歸函數的優勢是邏輯簡單清晰,缺點是過深的調用會致使棧溢出。

針對尾遞歸優化的語言能夠經過尾遞歸防止棧溢出。尾遞歸事實上和循環是等價的,沒有循環語句的編程語言只能經過尾遞歸實現循環。

Python標準的解釋器沒有針對尾遞歸作優化,任何遞歸函數都存在棧溢出的問題。

 

 

高階函數

變量能夠指向函數,函數的參數能接受變量,那麼一個函數就能夠接受另一個函數做爲參數,這種函數就稱爲高階函數。只須要知足如下任意一個條件,便是高階函數:

接受一個或多個函數做爲輸入

return返回另一個函數。 例如:

 

def func1():
        age=83
        def func2():
        return func2
val=func1()
print(val)
View Code  
def func():
    n=10
    def func2():
        print("func2",n)
    return func2
f=func()
print (f)
f()
View Code

 

 

函數的內置方法

http://www.runoob.com/python/python-built-in-functions.html

 

    內置函數    
abs() divmod() input() open() staticmethod()
all() enumerate() int() ord() str()
any() eval() isinstance() pow() sum()
basestring() execfile() issubclass() print() super()
bin() file() iter() property() tuple()
bool() filter() len() range() type()
bytearray() float() list() raw_input() unichr()
callable() format() locals() reduce() unicode()
chr() frozenset() long() reload() vars()
classmethod() getattr() map() repr() xrange()
cmp() globals() max() reverse() zip()
compile() hasattr() memoryview() round() __import__()
complex() hash() min() set()  
delattr() help() next() setattr()  
dict() hex() object() slice()  
dir() id() oct() sorted() exec 內置表達式

 

 

函數的做用域,命名空間(名稱空間)

名稱空間:存放名字和值的關係的空間起一個名字叫: 命名空間即名稱空間. 咱們的變量在存儲的時候就 是存儲在這片空間中的.  

####在python解釋器開始執行以後, 就會在內存中開闢一個空間, 每當遇到一個變量的時候, 就把變量名和值之間的關係記錄下來, 可是當遇到函數定義的時候, 解釋器只是把函數名讀入內存, 表示這個函數存在了,  至於函數內部的變量和邏輯, 解釋器是不關心的. 也就是說一開始的時候函數只是加載進來, 僅此而已, 只有當函數被調用和訪問的時候, 解釋器纔會根據函數內部聲明的變量來進行開闢變量的內部空間. 隨着函數執行完畢, 這些函數內部變量佔用的空間也會隨着函數執行完畢而被清空. 

def fun():   
    a = 10   
    print(a)
fun()
print(a)    # a不存在了已經
View Code

 命名空間分類:         

    1. 內置命名空間--> 存放python解釋器爲咱們提供的名字, list, tuple, str, int這些都是內置命名空間 (即builtins 內置模塊的名稱空間)

            2. 全局命名空間--> 咱們直接在py文件中, 函數外聲明的變量都屬於全局命名空間   (即globals  全局變量)    

            3. 局部命名空間--> 在函數中聲明的變量會放在局部命名空間 (即locals 包含局部變量和形參)

加載順序:

  1. 內置命名空間

  2. 全局命名空間

       3. 局部命名空間(函數被執行的時候)

取值順序:

       1. 局部命名空間

       2. 全局命名空間

       3. 內置命名空間

a = 10
def func():   
    a = 20   
    print(a)
 
func()  # 20
View Code

 

做用域:就是做用範圍(在python中一個函數就是做用域,全部的局部變量放置在其做用域中,代碼定義完成後,做用域已經生成,做用域鏈向上查找)

按照生效範圍來看分爲  全局做用域  和   局部做用域   

   全局做用域: 包含內置命名空間和全局命名空間. 在整個文件的任何位置均可以使用(遵循 從上到下逐⾏執行).

   局部做用域: 在函數內部可使用.             

做⽤域命名空間:         

  1. 全局做⽤用域:    全局命名空間 + 內置命名空間       

  2. 局部做⽤用域:    局部命名空間,只能在局部範圍內生效

  3.站在全局看:
    使用名字的時候:若是全局有,用全局的
    若是全局沒有,用內置的

4.爲何要有做用域?爲了函數內的變量不會影響到全局

5.globals方法:查看全局做用域的名字【print(globals())】

 locals方法:查看局部做用域的名字【print(locals())】

a = 10
def func():   
    a = 40   
    b = 20   
    def abc():       
        print("哈哈")   
        print(a, b)     # 這⾥裏里使⽤用的是局部做⽤用域   
        print(globals())    # 打印全局做用域中的內容   
        print(locals())     # 打印局部做用域中的內容
func()      
View Code

6.nonlocal讓內部函數中的變量在上一層函數中生效,外部必須有

# x=1
# def f1():
#     x=2
#     def f2():
#         # x=3
#         def f3():
#             # global x#修改全局的
#             nonlocal x#修改局部的(當用nonlocal時,修改x=3爲x=100000000,當x=3不存在時,修改x=2爲100000000 )
#                    # 必須在函數內部
#             x=10000000000
#         f3()
#         print('f2內的打印',x)
#     f2()
#     print('f1內的打印', x)
# f1()
# # print(x)
View Code

 7.函數名能夠用做參數

def func():
    print('func')

def func2(f):
    f()
    print('func2')
func2(func)
View Code

8.函數名能夠做爲函數的返回值 

def func():
    def func2():
        print('func2')
    return func2
f2=func()
f2()
#func2=func()
#func2()


2.

def f1(x):
    print(x)
    return '123'

def f2():
    ret = f1('s')  #f2調用f1函數
    print(ret)
f2()


3.
def func():
    def func2():
        return 'a'
    return func2   #函數名做爲返回值

func2=func()
print(func2())
View Code

  

函數進階-閉包

 

def func1():
    name = "alex"
    def func2():
        print(name)
        # 閉包
    func2()
func1()
# 結果: alex
View Code

使用__closure__來檢測函數是不是閉包. 使用函數名.__closure__返回cell就是
閉包. 返回None就不是閉包

def func1():
    name = "alex"
    def func2():
        print(name)
        # 閉包
    func2()
    print(func2.__closure__)
func1()
 
結果:
alex
(<cell at 0x0000020077EFC378: str object at 0x00000200674DC340>,)
返回的結果不是None就是閉包
View Code

 

   

函數進階-裝飾器

1.介紹:裝飾器本質上是一個Python函數,它可讓其餘函數在不須要作任何代碼變更的前提下增長額外功能,裝飾器的返回值也是一個函數對象。它常常用於有切面需求的場景,好比:插入日誌、性能測試、事務處理、緩存、權限校驗等場景。裝飾器是解決這類問題的絕佳設計,有了裝飾器,咱們就能夠抽離出大量與函數功能自己無關的雷同代碼並繼續重用

裝飾器=高階函數+函數嵌套+閉包

2.裝飾器的框架

from functools import wraps

def deco(func):
    @wraps(func) #加在最內層函數正上方
    def wrapper(*args,**kwargs):
        return func(*args,**kwargs)
    return wrapper
裝飾器的固定格式——wraps版
def outer(flag):
    def timer(func):
        def inner(*args,**kwargs):
            if flag:
                print('''執行函數以前要作的''')
            re = func(*args,**kwargs)
            if flag:
                print('''執行函數以後要作的''')
            return re
        return inner
    return timer

@outer(False)
def func():
    print(111)

func()
帶參數的裝飾器的固定格式
def wrapper1(func):
    def inner():
        print('wrapper1 ,before func')
        func()
        print('wrapper1 ,after func')
    return inner

def wrapper2(func):
    def inner():
        print('wrapper2 ,before func')
        func()
        print('wrapper2 ,after func')
    return inner

@wrapper2
@wrapper1
def f():
    print('in f')

f()
多個裝飾器裝飾同一個函數

 

3.列子
##********************裝飾器的列子************************

import time

def timmer (func) : #func=test  
  def wapper():
    start_time=time.time()
    func()    #就是在運行test()
    stop_time = time.time()
    print("函數的運行時間是%s" %(start_time-stop_time))
  return wapper

@timmer     #test=timeer(test)

def test():
time.sleep(2.5)
print("函數運行完畢")
test()
裝飾器的基本框架

##*************************** 加上參數的裝飾器的列子 ***********************

def timmer (func):    #func=test
    def wapper(*args,**kwargs):
        start_time=time.time()
        func(*args,**kwargs) #就是在運行test()
        stop_time = time.time()
        print("函數的運行時間是%s" %(start_time-stop_time))

    return wapper

@timmer   #test=timeer(test)
def test(name,age):
    time.sleep(2.5)
    print("test函數運行完畢,名字是(%s),年齡是(%s)" %(name,age,))
    return

test("",18)

@timmer
def test2 (name,age,gender):
    time.sleep(3)
    print("test2函數運行完畢,名字:(%s),年齡:(%s),性別:(%s)"%(name,age,gender))
test2("alex",18,"")
帶參數的裝飾器框架

##.*************************加上返回值的裝飾器的列子*************************

import time
def timer(func):
    def inner(*args,**kwargs):
        start = time.time()
        re = func(*args,**kwargs)
        print(time.time() - start)
        return re
    return inner

@timer   #==> func2 = timer(func2)
def func2(a):
    print('in func2 and get a:%s'%(a))
    return 'fun2 over'

func2('aaaaaa')
print(func2('aaaaaa'))
帶返回值的裝飾器

4. 防止裝飾器失效

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__)
print(index.__name__)
View Code

 

  

函數進階-生成器

列表的生成:

>>>>L= [x*x for x in range(10)]

>>>>L

[0,1,4,9,16,25,36,49,64,81]

 

1.生成器的定義:在Python中,這種一邊循環一邊計算的機制,稱爲生成器:generator。(本質就是一個迭代器)

元素的獲取

1.經過 next()打印出generator的每個元素,每次調用next(g),就計算出g的下一個元素的值,直到計算到最後一個元素,沒有更多的元素時,拋出StopIteration的錯誤:
next()next(g)gStopIteration
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
25
>>> next(g)
36
>>> next(g)
49
>>> next(g)
64
>>> next(g)
81
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
經過next()獲取 
2.使用for循環獲取元素
for
>>> g = (x * x for x in range(10))
>>> for n in g:
...     print(n)
... 
0
1
4
9
16
25
36
49
64
81
使用for循環得到元素  

 

2.建立:

1.把一個列表生成式的[]改爲(),就建立了一個generator

>>> g = (x * x for x in range(10)) >>> g <generator object <genexpr> at 0x1022ef630>
2.經過函數實現
##除第一個和第二個數外,任意一個數均可由前兩個數相加獲得:1, 1, 2, 3, 5, 8, 13, 21, 34, ...



def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'
 f = fib(6)
f
<generator object fib at 0x104feaaa0>

##若是一個函數定義中包含yield關鍵字,那麼這個函數就再也不是一個普通函數,而是一個generator:在generator的函數,在每次調用next()的時候執行,遇到yield語句返回,再次執行時從上次返回的yield語句處繼續執行。

將函數中的return換成yield就是生成器
# 函數
def func():
 
    print('這是函數func')
 
    return '函數func'
 
func()
 
# 生成器
def func1():
 
    print('這是函數func1')
 
    yield '函數func'
 
func1()
斐波那契數
__next__()來執行生成器
def func():
     print("111")
     yield 222
gener = func() # 這個時候函數不會執⾏. ⽽是獲取到⽣成器
ret = gener.__next__() # 這個時候函數纔會執⾏. yield的做⽤和return⼀樣. 也是返回數據
print(ret)
結果:
111
222
View Code
send方法:send和__next__()同樣均可以讓生成器執行到下一個yield
def eat():
    for i in range(1,10000):
        a = yield '包子'+str(i)
        print('a is',a)
 
        b = yield '窩窩頭'
        print('b is', b)
e = eat()
print(e.__next__())
print(e.send('大蔥'))
print(e.send('大蒜'))
View Code

send和__next__()區別:

send 和 next()都是讓生成器向下走一次

send能夠給上一個yield的位置傳遞值,不能給最後一個yield發送值,在第一次執行生成器的時候不能使用send()

第一次調用的時候使用send()也能夠可是send的參數必須是None

def func1():
    print('這是函數func1')
    f1 = yield '你好'
    print(f1)
    f2 = yield '我好'
    print(f2)
f = func1()
f.__next__()
f.send('你們好')
View Code


 

yield from:在python3中提供一種能夠直接把可迭代對象中的每個數據做爲生成器的結果進行返回
def func():
    lst = ['衛龍','老冰棍','北冰洋','牛羊配']
    yield from lst
g = func()
for i in g:
    print(i)


##有個小坑,yield from 是將列表中的每個元素返回,因此 若是寫兩個yield from 並不會產生交替的效果

def func():
    lst1 = ['衛龍','老冰棍','北冰洋','牛羊配']
    lst2 = ['饅頭','花捲','豆包','大餅']
    yield from lst1
    yield from lst2
     
g = func()
for i in g:
    print(i)
View Code

 

 

函數進階-迭代器

 

1.可迭代對象(Iterable

 可用於for循環的對象統稱爲可迭代對象 ,字符串,列表,元組,集合,字典,生成器 都是可迭代的。可使用isinstance()來判斷一個對象是不是Iterable對象

 2.迭代器定義(Iterator):

能夠被next()函數調用並不斷返回下一個值的對象稱爲迭代器。

理解:實際上,Python中的Iterator對象表示的是一個數據流,Iterator能夠被next()函數調用被不斷返回下一個數據,直到沒有數據能夠返回時拋出StopIteration異常錯誤。能夠把這個數據流看作一個有序序列,但咱們沒法提早知道這個序列的長度。同時,Iterator的計算是惰性的,只有經過next()函數時纔會計算並返回下一個數據。

三、可迭代協議:能夠被迭代要知足要求的就叫作可迭代協議。內部實現了__iter__方法

  iterable:可迭代的------對應的標誌

  什麼叫迭代?:一個一個取值,就像for循環同樣取值

        

四、迭代器協議:內部實現了__iter__,__next__方法

  迭代器大部分都是在python的內部去使用的,咱們直接拿來用就好了

  迭代器的優勢:若是用了迭代器,節約內存,方便操做

   dir([1,2].__iter__())是列表迭代器中實現的全部的方法,而dir([1,2])是列表中實現的全部方法,都是以列表的方式返回給咱們,爲了方便看清楚,咱們把他們轉換成集合,而後取差集,然而,咱們看到列表迭代器中多出了三個方法,那麼這三個方法都分別是幹什麼的呢?

 

 

 

 

相關文章
相關標籤/搜索