1. 程式人生 > 其它 >6、函式的高階運用

6、函式的高階運用

目錄

1、作用域

  • 所謂作用域,就是變數的有效範圍,就是變數可以在哪個範圍以內使用。
  • 有些變數可以在整段程式碼的任意位置使用,有些變數只能在函式內部使用,有些變數只能在 for 迴圈內部使用。
  • 變數的作用域由變數的定義位置決定,在不同位置定義的變數,它的作用域是不一樣的。

1.1、全域性變數與區域性變數

  • 定義在模組最外層的變數是全域性變數,它是全域性範圍內可見的,當然在函式裡面也可以讀取到全域性變數的
  • 定義在函式內部的變數是區域性變數,它僅在函式內部可見
A = 10   # 全域性變數,約定俗成需要大寫

def foo():
    b = 5  # 區域性變數
    print(A)  # a是全域性變數,函式內部可見,故結果是10
    print(b)  # b是區域性變數,函式內部可見,故結果是5

foo()

print(b)  # b是區域性變數,僅在函式內部可見,在函式外部不可見,此結果報錯

           結果:

          

  • 區域性作用域和全域性作用域的變數同名的情況
a=10  # 為了與區域性變數一致,這裡不大寫

def func():
    a=5  # 如果區域性變數與全域性變數重名,需要記住兩者是不一樣的變數,此時的區域性變數是一個新定義的區域性變數
    print(a)  # 故func()函式的結果是直接列印 5

func() # 5

print(a) # 10

# 結果是:
# 5
# 10

預設情況下,在區域性作用域對全域性變數只能進行 讀取修改內部元素(可變型別) 的操作,不能對全域性變數進行重新賦值。重新賦值的情況請參考上面 區域性作用域和全域性作用域的變數同名的情況

  • 讀取:
CITY_LISY=['BEIJING','SHANGHAI','GUANGZHOU','SHENZHEN']

def download():
    print(CITY_LISY) 
download()
# ['BEIJING', 'SHANGHAI', 'GUANGZHOU', 'SHENZHEN']
  • 修改內部元素:
CITY_LISY=['BEIJING','SHANGHAI','GUANGZHOU','SHENZHEN']

def download():
    CITY_LISY.append("DONGGUAN")
    print(CITY_LISY) 
download()
# ['BEIJING', 'SHANGHAI', 'GUANGZHOU', 'SHENZHEN', 'DONGGUAN']

1.2、global關鍵字

利用global關鍵字在函式內部重新賦值全域性變數

CITY_LISY=['BEIJING','SHANGHAI','GUANGZHOU','SHENZHEN']

def download(): 
    global CITY_LISY   # 利用global關鍵字在函式內部重新賦值全域性變數
    CITY_LISY=['北京','上海','廣州','深圳']  
    print(CITY_LISY) 
download()
# ['北京', '上海', '廣州', '深圳']
print(CITY_LISY) # 全域性變數已經被重新賦值
# ['北京', '上海', '廣州', '深圳']

2、函式的物件

Python中,函式是「頭等公民」。我們可以將函式賦值給變數,也可以將其作為引數傳入其他函式,將它們儲存在其他資料結構(如 dicts)中,並將它們作為其他函式的返回值。

即:可以把函式當成變數去使用

在函式名的末尾不新增(),只寫名稱的格式所表示的是函式本身。我們將其稱之為函式物件, 可以像值一樣將其代入到變數中去。

  • 可以將函式名(函式物件)賦值給變數,此時變數轉化成函式物件,可直接呼叫
def foo():
    print("foo")
bar=foo # 將函式賦值給變數bar,注意是函式名,不帶()
bar()  # 相當於呼叫了 foo()
# foo
bar  # bar變數就是函式,即bar就是函式物件
# <function __main__.foo()>
  • 把函式物件當做引數傳給另外一個函式
# 先定義一個函式,用來求輸入引數的10倍
def func1(x):
    r = x * 10
    return r
# 再定義一個函式,這個函式用來接收上一個函式
def func2(x, y, f):   # f表示此引數是函式作為的引數
    s = x + y + f(x) + f(y)
    return s
# 呼叫
func2(2, 5, func1) # s=2+5+2*10+5*10=77
# 77
  • 把函式物件當做另外一個函式的返回值
# 先定義一個函式,用來求輸入引數的10倍
def func1(x):
    r = x * 10
    return r
# 再定義一個函式,該函式返回剛才定義的第一個函式
def func2(f):  # f表示函式
    return f
r=func2(func1) # 將func2賦值給變數,注意內部func1函式是函式名,此時r是函式
r(5) # 呼叫,相當於:func2(func1(5)) = func1(5) = 5*10 = 50
# 50
r
# <function __main__.func1(x)>

3、高階函式

函式物件與變數平級,具有以下幾個特性:

  • 可以將函式物件賦值給變數,此時變數轉化成函式物件,可直接呼叫
  • 可以把函式物件當做引數傳給另外一個函式
  • 可以把函式物件當做另外一個函式的返回值

3.1、高階函式

一個函式可以接收另一個函式作為引數,這種函式稱之為高階函式

示例:

def add(x, y, f):  # add函式是高階函式,除了接受x,y兩個形參外,還接受了函式f作為引數
    return f(x) + f(y)

add(-5,6,abs)   # x=-5,y=6,f=abs ---> abs(-5) + abs(6) = 11
# 11

3.2、幾個重要的高階函式


3.2.1、map()函式

map()函式接收兩個引數,一個是函式,一個是序列。map()函式將傳入的函式一次作用到序列的每個原色,並將結果作為新的序列返回。

示例:

比如我們有一個函式f(x)=x*x,要把這個函式作用在一個序列[1,2,3,4,5,6,7,8,9]上,如下:

def f(x):
    return x*x

r=map(f,[1,2,3,4,5,6,7,8,9]) # 傳入函式物件,即f
r
# <map at 0x24d07c3bbb0>
type(r)
# map
list(r)
# [1, 4, 9, 16, 25, 36, 49, 64, 81]

示例:

序列[1,2,3,4,5,6,7,8,9],將序列的數字轉化為字串型別

a=[1,2,3,4,5,6,7,8,9]

r=map(str,a) # str即內建函式str()的函式物件

list(r)
# ['1', '2', '3', '4', '5', '6', '7', '8', '9']

3.2.2、reduce()函式

在python3中,reduce()不是內建函式,需要引用:from functools import reduce

reduce()函式,把一個函式作用在序列[x1,x2,x3,x4...]上,這個接受的函式必須是兩個引數,reduce()函式把結果繼續和序列的下一個元素做累積計算,其效果是:

reduce(func(x,y),[x1,x2,x3,x4]) = func(func(func(x1,x2),x3),x4)

示例:

from functools import reduce

def f(x, y):
    return x*y

r = reduce(f, [1, 2, 3, 4, 5])
r
# 120

圖解map函式遇reduce函式的區別


3.2.3、filter()函式

filter() 函式用於過濾序列。

  • map() 類似, filter() 也接收一個函式和一個序列。
  • map() 不同的是,filter() 把傳入的函式依次作用於每個元素,然後根據返回值是True還是False決定保留還是丟棄該元素。

示例:在一個list中,刪掉奇數,只保留偶數

def oushu(x):  # 定義一個函式oushu(),用來判斷輸入引數是否是偶數,如果是則返回True,如果否則返回False
    return x%2==0
oushu(6)
# True
oushu(5)
# False
r = filter(oushu,[1,2,3,4,5,6,7,8,9]) # 利用filter過濾掉奇數
r
# <filter at 0x24d07bff2e0>
list(r)
# [2, 4, 6, 8]

3.2.4、sorted()函式

sorted() 函式是python內建的,用來對序列排序的函式。

語法:

list = sorted(iterable, key=None, reverse=False)

  • iterable 表示指定的序列(可迭代物件)。
  • key 引數可以自定義排序規則,可選。
  • reverse 引數指定以升序(False,預設)還是降序(True)進行排序,可選。

sorted() 函式不會修改原本的序列,返回一個已經排好序的list。

示例:

對列表排序,返回的物件不會改變原列表

>>> list_1=[55,44,66,33,77]
>>> sorted(list_1) # 預設升序
[33, 44, 55, 66, 77]

>>> sorted(list_1,reverse=True) # reverse=True 降序
[77, 66, 55, 44, 33]

>>> list_1 # 繼續輸出 list_1 看下是否有變化
[55, 44, 66, 33, 77]

>>> r=sorted(list_1)
>>> type(r)
list

>>> r
[33, 44, 55, 66, 77]

示例:

對元組排序

tuple_1=(55,44,66,33,77)
sorted(tuple_1)
# [33, 44, 55, 66, 77]

示例:

根據自定義規則來排序,使用引數:key

# 根據字元的長短進行降序排序
chars=[ 'is',  'handsome','boy', 'bruce','a']
sorted(chars,key=lambda x:len(x),reverse=True) # lambda x:len(x) 匿名函式,返回每個字元的長度
# ['handsome', 'bruce', 'boy', 'is', 'a']

示例:

用一組tuple表示學生名字和成績:L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)],分別按名字排序(升序)與按成績從高到低排序。

L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
# 按名字排序(升序)
L1 = sorted(L, key=lambda x: x[0], reverse=False) # lambda x: x[0] 表示取得序列內每個元素的第一個內容
L1
# [('Adam', 92), ('Bart', 66), ('Bob', 75), ('Lisa', 88)]
# 按成績從高到低排序
L2 = sorted(L, key=lambda x: x[1], reverse=True)
L2
# [('Adam', 92), ('Lisa', 88), ('Bob', 75), ('Bart', 66)]

4、匿名函式

匿名函式指一類無須定義識別符號的函式或子程式。Python用 lambda 語法定義匿名函式,只需用表示式而無需申明。

語法如下:

lambda [引數1 [,引數2, ... 引數N]] : 表示式

如:

按照一般函式定義方法,定義一個求x平方的函式

def funca(x):
    return x**2

funca(10)
# 100

用匿名函式來表示就是:

f = lambda x: x**2  # 匿名函式也是物件,將匿名函式的函式物件賦值給變數f,使得f轉化為函式物件
f(10)
# 100

多引數

f2 = lambda a,b,c:a**2+b**2+c**2 
f2(1,2,3)
# 14

預設值

f3 = lambda a,b,c=2:a**2+b**2+c**2  # c變數預設為2,注意:預設值只能放在最後,否則會出錯
f3(1,3)
# 14

5、三元表示式

三元表示式

如有下面的程式:

a = 90

if a >= 60:
    result = "及格"
else:
    result = "不及格"

print(result)

# 及格

那麼用三元表示式來改寫,則如下所示:

a = 90
result= "及格" if a>=60 else "不及格"  # 三元表示式
print(result)

# 及格

匿名函式遇三元表示式的結合

一般寫法:

def f1(x):
    if x >= 60:
        result = "及格"
    else:
        result = "不及格"

    print(result)
    
f1(90)

# 及格

匿名函式遇三元表示式結合的寫法:

result=lambda x: "及格" if x>=60 else "不及格"
result(90)

# '及格'