函式是第一級公民
- Python 所有東西都是物件,包括函式、數字、字串、tuple、串列、字典
函式是 Python 的第一級公民
- 可以將函式指派給變數
- 當成引數傳給其他函式
- 在函式中將它們回傳
- 可以善用這種 Python 特點來做許多其他語言很難、或不可能做到的事情
函式用途:可以將函式當成「串列、tuple、集合、字典元素」使用
- 函式是不可變的,也可以將它們當成字典鍵
定義函式,沒有任何引數
>>> def 函式名稱():
print(數字)
>>> def answer(): # 定義函式 answer(),沒有任何引數
print(42) # 只會印出數字 42
>>> answer() # 執行函式,就會得到
42
定義函式,一個引數
>>> def 函式名稱(引數=函式)
引數=函式()
>>> def run_something(func): # 定義函式 run_something(),有一個引數稱為 func,是一個要執行的函式,會在裡面呼叫函式
func()
函式當成資料在使用
>>> def 函式名稱1():
print(數字)
>>> 函式名稱2(函式名稱1)
數字
>>> def answer():
print(42)
>>> answer()
42
>>> run_something(answer) # 將 answer 傳入 run_something(),代表將函式當成資料在使用,如同其他任何東西
42 # 請注意,傳入的是 answer,不是 answer()
「括號」代表「呼叫函式」,「不使用括號」會「將函式視為其他的物件」
>>> def answer():
print(42)
>>> answer()
42
>>> run_something(answer) # 將 answer 傳入 run_something(),代表將函式當成資料在使用,如同其他任何東西
42 # 請注意,傳入的是 answer,不是 answer()
- run_something(answer),請注意,傳入的是 answer,不是 answer()
- 在 Python 中,這些括號代表呼叫這些函式
- 不使用括號的話,Python 會將函式視為其他的物件 → 這是因為就像 Python 的其他東西,它是個物件
>>> type(run_something)
<class 'function'>
定義一個函式,印出兩個引數的總和
>>> def 函式名稱(數字引數1, 數字引數2)
print(數字引數1 + 數字引數2)
>>> def add_args(arg1, arg2): # 對函式傳入引數,定義函式 add_args()
print(arg1 + arg2) # 印出兩個數字引數(arg1 與 arg2)的總和
>>> type(add_args) # 什麼是 add_args()
<class 'function'>
定義一個函式,使用三個引數
- 定義函式:run_something_with_args() 函式
- 使用三個引數:
- func - 待執行的函式
- arg1 - func 的第一個引數
- arg2 - func 的第二個引數
>>> def 函式名稱(待執行的函式, 引數1, 引數2):
待執行的函式(引數1, 引數2)
>>> def run_something_with_args(func, arg1, arg2): # 呼叫 run_something_with_args()時,「呼叫方傳入的函式」會被指派給「func參數」,arg1與arg2會取得引數列中的值
func(arg1, arg2) # 執行 func(arg1, arg2) 會用這些引數來執行該函式,因為括號告訴 Python 這麼做
>>> def run_something_with_args(func, arg1, arg2):
func(arg1, arg2)
>>> def add_args(arg1, arg2):
print(arg1 + arg2)
# 將函式名稱 add_args與引數 5 與 9,傳入 run_something_with_args() 來測試
# 在函式 run_something_with_args() 裡面,「函式名稱引數 add_args」被指派給「參數func」
# 5 被指派給 arg1,9 被指派給 arg2,因此它會執行:add_args(5, 9)
>>> run_something_with_args(add_args, 5, 9)
14
內建函式 sum() + args/kwargs
sum(),是內建的 Python 函式,可計算它的可迭代數值(整數或浮點數)引數的總和值
sum() 函式來計算「引數總和」
>>> def sum_args(*args): # 定義函式 → 函式取用任意數量的位置引數 → 使用 sum() 函式來計算「引數總和」
return sum(args) # 回傳總和
內建函式 sum() + args/kwargs
>>> def 函式名稱(函式, *args):
return func(*args)
>>> def run_with_positional_args(func, *args): # 定義新函式 run_with_positional_args(),取用一個函式 func,任意數量的引數 *args
return func(*args)
>>> def sum_args(*args):
return sum(args)
>>> run_with_positional_args(sum_args, 1, 2, 3, 4) # 函式 run_with_positional_args(),呼叫 sum_args()
10
內部函式
「其他函式」裡面定義「函式」
>>> def 函式名稱1(引數1, 引數2):
def 函式名稱2(引數3, 引數4):
return 引數3 + 引數4
return 函式名稱2(引數1, 引數2)
>>> 函式名稱2(引數1, 引數2)
引數1 + 引數2
>>> def outer(a, b):
def inner(c, d):
return c + d
return inner(a, b)
>>> outer(4, 7)
11
內部函式,可以避免編寫重複的迴圈或程式碼
如果想要在「某個函式」裡面執行「多次複雜的工作」,內部函式非常實用 → 可以避免編寫「重複的迴圈或程式碼」
>>> def 函式名稱1(引數1):
def 函式名稱2(引數2):
return "文字: '%s'" % 引數2
return 函式名稱2(引數1)
>>> 函式名稱1(引數1)
"文字: '引數2'"
>>> def knights(saying):
def inner(quote):
return "We are the knights who say: '%s'" % quote
return inner(saying)
>>> knights('Ni!')
"We are the knights who say: 'Ni!'"
%s
>>>name = raw_input("who are you?")
print "hello %s" % (name,)
`
The %s token allows me to insert (and potentially format) a string. Notice that the %s token is replaced by whatever I pass to the string after the % symbol. Notice also that I am using a tuple here as well (when you only have one string using a tuple is optional) to illustrate that multiple strings can be inserted and formatted in one statement.
(From https://stackoverflow.com/questions/997797/what-does-s-mean-in-python)
Closure
- 內部函式可以扮演 closure 的角色
- 這是一種由「其他的函式動態」生成的函式,可以「更改或記得」函式之外的程式所建立的「變數的值」
內部函式 v.s. closure 的差異
- 內部函式
>>> def 函式名稱1(引數1):
def 函式名稱2(引數2):
return "文字: '%s'" % 引數2
return 函式名稱2(引數1)
>>> 函式名稱1(引數1)
"文字: '引數2'"
>>> def knights(saying):
def inner(quote):
return "We are the knights who say: '%s'" % quote
return inner(saying)
>>> knights('Ni!')
"We ar
* Closure
```python
>>> def 函式名稱1(引數1):
def 函式名稱2(): # 函式名稱2(),沒有引數,直接使用外面的參數「引數1」
return "文字: '%s'" % 引數1
return 函式名稱2 # return 函式名稱2,「回傳」函式名稱,而「不是呼叫」函式名稱
>>> def knights2(saying):
def inner2():
return "We are the knights who say: '%s'" % saying
return inner2 # 回傳 inner2 函式名稱,而不是呼叫它
# 原本 inner() 轉換成 inner2() 的closure。inner2() 直接使用外面的 saying 參數,而不是以引數來取得
# inner2() 函式知道被傳入的 saying 的值,並且會記得它
# return inner2 這一行會回傳專用的 inner2 函式複本(而不是呼叫它)→ 那是一個 closure:一個「被動態建立出來」,會記得自己來自哪裡的函式
呼叫 knights2()兩次,並使用不同的引數
>>> def knights2(saying):
def inner2():
return "We are the knights who say: '%s'" % saying
return inner2
>>> a = knights2('Duck')
>>> b = knights2('Hasenpfeffer')
a 跟 b 是什麼
>>> type(a)
<class 'function'>
>>> type(b)
<class 'function'>
a、b 是函式,但它們也是 closure
>>> a
<function knights2.<locals>.inner2 at 0x100562e18>
>>> b
<function knights2.<locals>.inner2 at 0x10239c620>
呼叫 a、b,它們會記得自己被 knights2 建立時「所使用的saying」
>>> a()
"We are the knights who say: 'Duck'"
>>> b()
"We are the knights who say: 'Hasenpfeffer'"
匿名函式:lambda()函式
在 Python 中,lambda 函式是一種以「一行陳述式」來表示的「匿名函式」→ 可以用它來取代一般的小函式
【範例】製作一個使用一般函式的範例【定義函式 + 引數 + for...in + print()】
為了說明,先來製作「使用一般函式」的範例。
- 定義函式:edit_story()
- 引數:
- words(一串單字)
- func(函式,套用至 words 內的每一個單字)
>>> def 函式名稱(引數1s, 套用引數1s的函式名稱)
for 引數1 in 引數1s:
print(套用引數1s的函式名稱(引數1))
>>> def edit_story(words, func):
for word in words:
print(func(word))
定義函式 + 串列 + 函式(一個「套用至每一個單字」的函式)+ capitalize()
>>> def 函式名稱1(引數1s, 套用引數1s的函式名稱)
for 引數1 in 引數1s:
print(套用引數1s的函式名稱(引數1))
>>> 串列名稱 = ['字串1', '字串2', '字串3', '字串4']
>>> def 函式名稱2(引數1): # 函式,它會將「每個字的第一個字母」改為大寫,並加上一個驚嘆號
return 引數1.capitalize() + '!'
>>> 函式名稱1(串列名稱, 函式名稱2)
字串1!
字串2!
字串3!
字串4!
>>> def edit_story(words, func):
for word in words:
print(func(word))
>>> stairs = ['thud', 'meow', 'thud', 'hiss']
>>> def enliven(word): # give that prose more punch
return word.capitalize() + '!'
>>> edit_story(stairs, enliven) # 混合以上材料
Thud!
Meow!
Thud!
Hiss!
lambda
>>> def 函式名稱1(引數1s, 套用引數1s的函式名稱)
for 引數1 in 引數1s:
print(套用引數1s的函式名稱(引數1))
>>> 串列名稱 = ['字串1', '字串2', '字串3', '字串4']
>>> 函式名稱1(串列名稱, lambda 引數1: 引數1.capitalize() + '!')
字串1!
字串2!
字串3!
字串4!
>>> def edit_story(words, func):
for word in words:
print(func(word))
>>> stairs = ['thud', 'meow', 'thud', 'hiss']
# enliven()函式很簡短,所以可以將它換成 lambda
>>> edit_story(stairs, lambda word: word.capitalize() + '!') # lambda 會取用一個引數,在此稱為word。
Thud! # 在冒號與結束括號之間的所有東西,就是函式的定義 → word.capitalize() + '!'
Meow!
Thud!
Hiss!
使用真正的函式 v.s. 使用lambda
- 通常使用這種真正的函式(如:enliven()),會比使用 lambda 還要清楚
- 最常用到 lambda 的情況是:需要定義「許多小型程式」,而且要記得該如何稱呼它們時
- 特別是,可以在「圖形化使用者介面」使用 lambda 來定義回呼函式(參考本書 附錄A的範例)