函式是第一級公民

  • 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'>

定義一個函式,使用三個引數

  1. 定義函式:run_something_with_args() 函式
  2. 使用三個引數:
    • 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的範例

results matching ""

    No results matching ""