# 函數(shù)(2)
## 1.函數(shù)類型
函數(shù)也是一種類型,我們自定義的函數(shù)就是函數(shù)對(duì)象,函數(shù)名保存了函數(shù)對(duì)象的引用(地址)
```
def test():
? ? print('我是測(cè)試函數(shù)')
print(test)? #函數(shù)名是變量,指向了函數(shù)對(duì)象
pf = test? ? #pf變量也指向了函數(shù)對(duì)象,所以也可以通過pf調(diào)用test函數(shù)
pf()
```
## 2. 匿名函數(shù)
不再使用def 函數(shù)名()這種形式定義函數(shù),而是使用lambda來創(chuàng)建匿名函數(shù)
特點(diǎn):
- lambda只是一個(gè)表達(dá)式,函數(shù)體比def簡(jiǎn)單的多
- lambda的函數(shù)體不再是代碼塊
- lambda只有一行,增加運(yùn)行效率
語法:
```
lambda [arg1,arg2....argn]:表達(dá)式
add = lambda a,b:a + b
print(add(3,5))
```
## 3.傳入函數(shù)
一個(gè)函數(shù)就可以接收另一個(gè)函數(shù)作為參數(shù),這種函數(shù)就稱之為高階函數(shù),也可以稱之為傳入函數(shù)。可以實(shí)現(xiàn)通用編程,排序等復(fù)雜功能
~~~
#傳入函數(shù),高階函數(shù)
#能被2整除數(shù)的和
def sum_even(n):
? ? sum = 0
? ? for i in range(1,n+1):
? ? ? ? if i % 2 == 0:
? ? ? ? ? ? sum += i
? ? return sum
#能被7整除的數(shù)的和
def sum_seven(n):
? ? sum = 0
? ? for i in range(1,n+1):
? ? ? ? if i % 7 == 0:
? ? ? ? ? ? sum += i
? ? return sum
#能被3和5整除,但不能7整除的數(shù)的和
def sum_fifteen(n):
? ? sum = 0
? ? for i in range(1,n+1):
? ? ? ? if i % 15 == 0 and i % 7 != 0:
? ? ? ? ? ? sum += i
? ? return sum
#通用求和函數(shù)
def sum1(n,callback):
? ? '''
? ? 功能:求滿足callback規(guī)定條件的數(shù)的和
? ? :param n: 大于0的整數(shù)
? ? :param callback: 用于判斷一個(gè)數(shù)是否滿足指定條件,由調(diào)用者傳入,有一個(gè)參數(shù),形如:def callback(n)
? ? :return: 求和的結(jié)果
? ? '''
? ? sum = 0
? ? for i in range(1,n+1):
? ? ? ? if callback(i):
? ? ? ? ? ? sum += i
? ? return sum
print(sum1(100,lambda x:x%2==0))
print(sum1(100,lambda x:x%7==0))
print(sum1(100,lambda x:x%15==0 and x % 7 != 0))
~~~
## 4.閉包
我們可以在一個(gè)函數(shù)中再定義一個(gè)函數(shù),在函數(shù)內(nèi)部定義的函數(shù)稱之為***內(nèi)部函數(shù)***,內(nèi)部函數(shù)只能在函數(shù)內(nèi)使用,不會(huì)污染外部空間。定義內(nèi)部函數(shù)的函數(shù)稱之為***外部函數(shù)***,這樣的定義構(gòu)成函數(shù)的嵌套
~~~
def outter(a): #外部函數(shù)
? ? x = 10
? ? def inner(y):? #內(nèi)部函數(shù)
? ? ? ? print(x + y)
? ? inner(a)
outter(20)
~~~
- 內(nèi)部函數(shù)只能在外部函數(shù)里調(diào)用,外界無法直接調(diào)用內(nèi)部函數(shù)
在一個(gè)外部函數(shù)中定義了一個(gè)內(nèi)部函數(shù),內(nèi)部函數(shù)里引用了外部函數(shù)的變量,并且外部函數(shù)的返回值是內(nèi)函數(shù)的引用。這樣內(nèi)部函數(shù)和其執(zhí)行所需的環(huán)境變量就構(gòu)成了一個(gè)***閉包***。
一般情況下,如果一個(gè)函數(shù)結(jié)束,函數(shù)的內(nèi)部所有東西都會(huì)釋放掉,局部變量都會(huì)消失。但是閉包是一種特殊情況,如果外函數(shù)在結(jié)束的時(shí)候發(fā)現(xiàn)有自己的局部變量將來會(huì)在內(nèi)部函數(shù)中用到,就把這個(gè)局部變量綁定給了內(nèi)部函數(shù),然后自己再結(jié)束。
~~~
def outter(a): #外部函數(shù)
? ? x = a
? ? def inner(y):? #內(nèi)部函數(shù)
? ? ? ? return x + y? #引用外部變量
? ? return inner? #返回內(nèi)部函數(shù)(閉包)
pf = outter(20)
print(pf(10))? #30
print(pf(20))? #40
~~~
在閉包中無法直接修改外部變量x的值
~~~
def outter(a): #外部函數(shù)
? ? x = a
? ? def inner(y):? #內(nèi)部函數(shù)
? ? ? # x += 10? #UnboundLocalError: local variable 'x' referenced before assignment
? ? ? ? return x + y
? ? return inner
~~~
在python3中可以通過nonlocal關(guān)鍵字聲明一下x,表示這個(gè)變量不是局部變量,需要向上一層變量空間找這個(gè)變量。
~~~
def outter(a): #外部函數(shù)
? ? x = a
? ? def inner(y):? #內(nèi)部函數(shù)
? ? ? ? nonlocal x
? ? ? ? x += 10
? ? ? ? return x + y
? ? return inner
~~~
## 5.偏函數(shù)
當(dāng)一個(gè)函數(shù)有大量參數(shù),調(diào)用的時(shí)候非常不方便,可以使用偏函數(shù)技術(shù),將一些參數(shù)固定(給默認(rèn)值),達(dá)到簡(jiǎn)化函數(shù)調(diào)用的目的。
~~~
import functools
def test(a,b,c,d):
? ? print(a, b, c, d)
#從前面固定參數(shù),使用位置參數(shù)就行,1=>a,2=>b
test1 = functools.partial(test,1,2)
test1(3,4)? #3=>c? 4=>d
#從后面固定參數(shù),需要使用關(guān)鍵字參數(shù)
test2 = functools.partial(test,c=3,d=4)
test2(1,2)? #1=>a 2=>b
#如果固定的參數(shù)不連續(xù),則需使用關(guān)鍵字參數(shù)固定
test3 = functools.partial(test,b=2,d=4)
test3(a=1,c=3) #需要使用關(guān)鍵字參數(shù),否則會(huì)報(bào)錯(cuò)
~~~
## 6.變量的作用域
程序中的變量并不是在任意的位置都可以隨意訪問,在哪里可以訪問取決于這個(gè)變量的作用域,變量的作用域指的是變量在那段代碼中可以使用,可以使用變量的那段代碼就是變量的作用域。在python中,只有函數(shù)/類/模塊才引入作用域,if/elif/else , while/for,try/except等并不會(huì)引入新的作用域
~~~
#if語句不引入新作用域,msg在外面可以使用
if True:
? ? msg = "message"
print(msg)
~~~
### 6.1 變量作用域的分類
按照作用域劃分,可以分為:
- L:Local,局部作用域?
- E:Enclosing,閉包作用域【閉包外的函數(shù)中定義的變量】
- G:Global,全局作用域? 在所有函數(shù)外定義的變量
- B:Built-in,內(nèi)建作用域【內(nèi)置作用域】
~~~
#1 局部作用域?
#局部變量只能在函數(shù)內(nèi)部使用,外部無法引用
#局部變量的作用域從定義開始到函數(shù)體結(jié)束
def demo():
? ? num = 20? #局部變量?
? ? print(num)
demo()
#print(num) 錯(cuò)誤
#閉包作用域
#
def outter():
? x = 10? #函數(shù)作用域,從定義開始到本函數(shù)結(jié)束
? def inner():
? ? ? y = x? #在閉包中可以引用
? ? ? print(y)
? return inner
pf = outter()
pf()? #執(zhí)行閉包
print(pf.__closure__)
#全局作用域
x = 100? ? #全局作用域? 從定義開始到本文件結(jié)束
def demo():
? print(x)
print(x)
#內(nèi)建作用域,是指系統(tǒng)內(nèi)建的函數(shù)或常量,在系統(tǒng)載入時(shí)加載,在所有模塊中都可以直接引用
#比如說系統(tǒng)函數(shù)
print(max(1,2,3))? #max函數(shù)就是內(nèi)建作用域? 哪里都可以引用
def? demo():
? ? x = 30
? ? y = 50
? ? print(max(x, y))
~~~
### 6.2 變量作用域查找規(guī)則
以 L --> E --> G -->B 的規(guī)則查找,即:在局部找不到,便會(huì)去局部外的局部找(例如閉包),再找不到就會(huì)去全局找,最后到內(nèi)建作用域中找。
### 6.3 全局變量和局部變量
定義在函數(shù)內(nèi)部的變量擁有一個(gè)局部作用域,被稱為局部變量
定義在函數(shù)外面的變量擁有一個(gè)全局作用域,被稱為全局變量
~~~
total = 0? #全局變量
def sum(arg1,arg2):
? ? total = arg1 + arg2? #局部變量
? ? print("函數(shù)內(nèi)部:",total)
? ? return total
sum(10,20)
#print(total1)
print("函數(shù)外部:",total)
num = 1
def fun1():
? ? print(num) #UnboundLocalError: local variable 'num' referenced before assignment
? ? num = 123
? ? print(num)
fun1()
~~~
### 6.4 global和nonlocal
~~~
#1.在Python中,當(dāng)內(nèi)部作用域想修改外部作用域的變量的時(shí)候,則就要使用global關(guān)鍵字進(jìn)行聲明
num = 1
def fun1():
? ? global num? #告訴編譯器,此處的num是全局變量
? ? print(num)? #1
? ? num = 123
? ? print(num)? #123
fun1()
a = 10
def test():
? ? global? a
? ? a = a + 1
? ? print(a)
test()
#2.如果要修改函數(shù)作用域中的變量,則使用nonlocal
#需要明確的是,nonlocal關(guān)鍵字定義在閉包里面
x = 0? #全局作用域
def outer():
? ? x = 1? #函數(shù)作用域
? ? def inner():
? ? ? ? nonlocal x
? ? ? ? x = 2? #局部作用域
? ? ? ? print("inner:",x)? #2
? ? inner()
? ? print("outer:",x)? #1---->2
outer()
print("全局:",x)? #0
#nonlocal關(guān)鍵字:聲明了該變量不只是在inner函數(shù)內(nèi)部才有效,而是在整個(gè)outer函數(shù)中有效
~~~