1. 程式人生 > >python學習筆記3 函式 閉包 裝飾器

python學習筆記3 函式 閉包 裝飾器

函式

def foo3():#函式名與引數
#函式體
    cumsum=0
    for i in range(15):
        cumsum+=i
        
#return返回的是物件,(如返回物件數>1返回元組)
    return cumsum,cumsum*2
    return cumsum-1   #不返回


##yield  生成器
def foo4():
    for i in range(5):
        yield i
        yield 'f' #再加入一個yield看看會不會被執行
        i+=1
   
g1=
foo4()#返回(generator object foo4 at 0x0000000002555E60) type(g1)#<class 'generator'> next(g1)#繼續上一次的位置,進入下一層迴圈 #執行完最後一次迴圈,結束 yield語句並丟擲StopIteration 異常

函式的引數

  • 位置引數
  • 關鍵字引數
  • 位置引數包裹*
  • 關鍵字引數包裹**
##位置引數
def foo3(companyName,websiteUrl):#位置引數的定義
    print('公司名:',companyName,'公司網址:',websiteUrl)
foo3('七月線上'
,'http://www.julyedu.com') foo3('http://www.julyedu.com',websiteUrl='七月線上') #位置引數,對位置敏感,傳遞時不用指定引數名 ##關鍵字引數 def foo4(companyName,websiteUrl,intro='專注AI教育'):#關鍵字引數的定義 print('公司名:',companyName,'公司網址:',websiteUrl,'介紹:',intro) foo4('七月線上','http://www.julyedu.com')#不傳關鍵字引數,函式依舊可以被呼叫,採用預設值 foo4('七月線上','http://www.julyedu.com'
,intro='國內領先的AI教育平臺')#傳關鍵字引數,覆蓋預設值 ##位置引數包裹* def mysum1(a,b,c): return a+b+c mysum1(5,7,15) #mysum1(5,7) #報錯,因為傳入的引數少於函式定義時的個數 #mysum1(5,7,15,22) #報錯,因為傳入的引數多於函式定義時的個數 #這時我們就需要使用包裹進行接收 def mysum2(*args): print(type(args)) # #<class 'tuple'> return sum(args) mysum2(5,7,15,22,33)#正常呼叫 mysum2(5,7)#正常呼叫 #傳遞引數時使用包裹-位置引數 l1=[1,5,6,7,2,5,3.5,9,3,4] mysum2(*l1) #將列表打碎 每一個元素是一個tuple 返回的是(1,5,6,7,2,5,3,5,9,3,4) #zip案例 l1=[(1,3),(2,4)] list(zip(*l1)) #將列表l1打碎 再拼接起來 ##那同樣如果想一次性傳入不定長關鍵字引數,也可以使用一個包裹進行接收 ##關鍵字引數包裹** def filmInfo(**kwargs): print(type(kwargs)) #<class 'dict'> for key,values in kwargs.items(): print(key,':',values) filmInfo(film='羞羞的鐵拳',box=3.5) filmInfo(film='羞羞的鐵拳',box=3.5,rate=7.9) d1={'羞羞的鐵拳':3.5,'雷神3':3.1,'戰狼2':60} #一定要注意字典和關鍵字引數傳入的不同 filmInfo(**d1) #包裹解包順序 #首先拆位置引數包裹,按順序給必選,預設,可變。 #再抓給關鍵字引數包裹 def scoreReport(name,age,*args,course='python',**kwargs): print('個人資訊:',name,age) for item in args: print(item) print('課程資訊:',course) print('每節課成績:') for key,value in kwargs.items(): print (key,value) scoreReport('xiaoming',22,'高中部','三年二班',Lesson1=80,Lesson2=85) scoreReport('xiaoming',22,'三年二班',course='machine learning',Lesson1=80,Lesson2=85)

引數傳遞的處理

重要

##值傳遞,引數本身不會被修改
a=7
b='julyedu'
print('before reference:',a,b)    

def foo1(a,b):
    a+=1
    b+='.com'
    print('In foo1 a,b has changed to',a,b)

foo1(a,b)
print('after reference:',a,b)   #after reference: 7 julyedu   #非常重要
#結論:值傳遞時,變數傳遞給函式後,函式複製一份,不會影響原有變數


##指標傳遞引數,會修改引數本身
l1=[1,2,3.4]
d1={'name':'jack'}
def foo2(a,b):
    a.pop()
    b['age']=22
    
print('before reference:',l1,d1)   #before reference: [1, 2, 3.4] {'name': 'jack'}

foo2(l1,d1)
print('after reference:',l1,d1) #after reference: [1, 2] {'name': 'jack', 'age': 22}
#結論:指標(或引用)傳遞時,變數傳遞給函式的是及引用,該引用可以改變原變數

變數的作用域

全域性變數 區域性變數 區域性變數可以覆蓋全域性變數 區域性作用域->全域性作用域

偏函式PFA

使用場景:如果一個函式的引數很多,而在每次呼叫的時候有一些又經常不需要被指定時,就可以使用偏函式(近似理解為預設值) 語法:partical(func,*args,**keywords) 使用:from functools import partial import functools

#Partial function application(PFA)
# 只設置了一部分的引數的函式
# 固定一部分引數,使得被呼叫時,某些引數被固定住了。

#例如我們要定義一個函式將傳進來的16進位制的字串,轉換為10進位制的
def hex2int(num):
    return  int(str(num),base=16)#base為關鍵字引數,這個在呼叫int這個函式時,固定給16。因為這個函式就是用來轉換16進位制到10進位制

hex2int('F')#這時呼叫,相當於實際上會把10作為*args的一部分自動加到左邊,也就是:int('F',base=16),這樣就少寫了一個函式

#這時也可以使用偏函式,固定int函式的關鍵字引數base,可以這樣定義:
import functools
hex2int2=functools.partial(int,base=16)
hex2int2('A')   #10

#偏函式可以固定位置引數
max100=functools.partial(max,100)#定義一個叫max100的偏函式,將這個偏函式的第一個值固定為100
max100(101)#這時呼叫它,傳入的值,都會與100進行比較反並返回。

type(max100)#偏函式的型別與普通函式不一樣   #<class  'functools.partial'>

遞迴函式

#遞迴的方法求一個列表中數值的累加
def foo1(num):
    if len(num)==1:
        return num[0]
    else:
        return num[0]+foo1(num[1:]) #切片
 
foo1([1,2,31,5,6,55])     #100

#使用遞迴分解質因數
l1=[]
def fenji(num):
    num=int(num)
    for i in range(2,num):
            if num%i==0:
                l1.append(i)
                nextv=int(num/i)
                fenji(nextv)
                break#這裡的break不加就會硬迴圈60次                
    return l1

fenji(60)

#階乘
def factorial(n):
    if n==1:
        return 1
    else:
        return n*factorial(n-1)
factorial (5)

匿名函式lambda

#匿名函式
print(type(lambda a,b:a**b ))   #function
#不用寫return

#使用1:作為正常函式使用,不推薦
foo=lambda x,y:x+y #不用寫return
print(foo(5,6))  #11

#使用2:lambda關鍵字定義在高階函式的引數位上
d1={'china':15,'India':9,'USA':2,'Japan':1.5}
sorted(d1.items(),key=lambda x:(x[0],x[1]))#按d1.items()第0個元素升序,國家名
# sorted(d1.items(),key=lambda x:(x[1]))#按d1.items()第1個元素升序,人口數

高階函式

函式名可以向變數一樣,在函式中作為引數,傳來傳去

##高階函式
def mycalucate(num,func): 
    return func   #num作為引數再傳入func中

l1=[5,8,3,6,9,15,22]
mycalucate(l1,max)
mycalucate(l1,min)
mycalucate(l1,sum)
mycalucate(l1,reversed)

##回撥函式
#函式作為呼叫函式的結果返回
def callbackfunc(*num):
    return max   #返回的是函式 所以叫回調函式
list(callbackfunc(53,5,33))  #53

BIFs-高階函式

filter:對每個元素做過濾 map:對每個元素做對映 一種更高階的列表推導式 reduce:兩兩傳給func Python 3.x中,reduce()函式已經被從全域性名字 空間裡移除了,它和partical一樣被放置在fucntools模組中。使用前需要呼叫

l1=[2,3,5,7,3,4,5,9,'julyedu.com',[2,3,3],5,2]
##filter
#語法:filter(function,list)
# 函式 f 的作用是對每個元素進行判斷,返回 True或 False
# filter()根據判斷結果自動過濾掉不符合條件的元素,返回由符合條件元素組成的新list。
def myfilter(x):
    if x>60:
        return True
    else:
        return False
        
filter(myfilter,l1)
list(filter(lambda x:True if type(x)==str else False,l1))

##map
# 語法:map(function, list)
#讓list的每一個元素依次呼叫function函式,並獲取返回值存入一個新的list中。
map(lambda x:(x,l1.count(x)),l1)    #統計列表中每個元素出現的次數  輸出(x,它出現的次數) 

## reduce
# 語法:reduce(function, list)
#函式function必須包含兩個引數,optional可選,如果存在則表示初值為optional 
#reduce()對list的每個元素反覆呼叫函式f,並返回最終結果值。
from functools import reduce
reduce(lambda a,b:a+b,[1,2,3,4,5,6,7,8])  #lambda輸入的是兩個引數  輸出的是列表的求和

閉包Closure

nums_in_global=[15,2,3,9,3.2]#宣告一個全域性
def foo1(nums_in_function):
    print('nums_in_function此時在是foo1中,可以被 訪問:',nums_in_function)
    def foo2():
        return max(nums_in_function)#雖然沒有給foo2傳入任何引數,但foo2卻能訪問到foo1中的變數nums_in_function
#         return max(nums_in_global)#雖然沒有給foo2傳入任何引數,但foo2卻能訪問到全域性變數nums_in_global
    return foo2    #返回定義函式裡面的函式物件

#呼叫
foo1([5,3,8])()

裝飾器

現有如下 三個函式
def foo1():
    print ('this is foo1 function')

def foo2():
    print ('this is foo2 function')

def foo3():
    print ('this is foo3 function')
    
#現在想為每一個函式都新增一個列印當前時間的功能
#我們可以這樣解決
import datetime
def extrafoo(func):
    def inner():
        print('from inner to execute:',func.__name__)   #func.__name__是傳入引數的名字
        print('the',func.__name__,'result:',func())
        print('extra:',datetime.datetime.now())
    return inner

@extrafoo   #裝飾器特性,被裝飾的函式定義之後立即執行。
def foo1():
    return 'this is foo1 function--'

#@是python裝飾器的簡便寫法,也叫語法糖
#裝飾器語法糖在要被包裹的函式前宣告。@後面的函式名,是包裹下邊函式的函式名extrafoo
#該語法糖省略了
#decorated=foo(test)
#decorated()

# 裝飾器在Python使用如此方便都要歸因於
# Python的函式能像普通的物件一樣能作為引數傳遞給其他函式
# 可以被賦值給其他變數,可以作為返回值,可以被定義在另外一個函式內。

foo1()