--- title: Python Function tags: python, function --- [TOC] --- ## Function ### Basic Function Prototype ```python= def FunctionName(Parameter List): Function Body ``` ### Return None Type 這邊需要注意的是,function可以不回傳東西,因此若沒有明確指定要回傳甚麼,都是回傳 `None` 這個特殊型別 ```python= # 以下三種寫法回傳值皆為 None def fun1(): print('Hello') def fun2(): print('Hello') return def fun3(): print('Hello') return None ``` ### Multiple Return Values Python 允許同時回傳多個回傳值,以**Tuple**形式回傳 ```python= def fun(a, b, c): return a, a+b, a+b+c # 等同於 return (a, a+b, a+b+c) n = fun(1,2,3) # n = (1,3,6) n1, n2, n3 = fun(1,2,3) # n1=1, n2=3, n3=6 n1, n3 = fun(1,2,3) # Try and Test(T1) ``` ### Parameter has Default Value Python 允許有Default Value,規則是通用的一律從右邊開始省略 ```python= def fun(a, b=5, c=10): return a+b+c n1 = fun(1,2,3) # n1=6 n2 = fun(1,2) # n2=13 n3 = fun(1) # n3=16 ``` ### Position & Keyword Arguments 在Python傳遞參數時,除了用位置對齊以外,還可以用parameter的名字來指定 注意:**position argments 不能在 keyword arguments 的右邊** ```python= def say(name, msg): print(name, msg) say('Tom', 'Hi') == say(msg='Hi', name='Tom') == say('Tom', msg='Hi') ``` ### Built-in Functions [Python documentation](https://docs.python.org/3/library/functions.html) - `abs(num)` - Return the absolute value of `num` - `sum(iterable, start=0)` - `id(obj)` - Return a **unique** number(not address) about the `obj` - `type(obj [, base, dict])` - `type(obj)` Return the class name of the obj - `type(obj, base, dict` Create a new class - `int(n) float(n)` - `oct(n) hex(n)` - Return type is string - `complex(real=0, imag=0)` - `str(x) bytes(x)` - `list(iterable) tuple(iterable) dict(iterable)` - 底下Inbuilt Data Structure有介紹 - `isinstance(obj, class | tuple)` - 可以判斷obj是否為該class或是該class的子類別 - Return boolean - `setattr(obj, name, value)` - 相當於`obj.name = vlaue` - `getattr(obj, name)` - `round(num)` - 做四捨五入 - `filter(fun, iterable)` ```python= # 習慣將回傳值變成偶數 round(1.5) # 2 round(2.5) # 2 ``` - [eval](https://realpython.com/python-eval-function/) --- ### Advanced Function Prototype > 你可以不會寫,但是遇到必須看得懂 ```python= def FunctionName(arg, *args, **kwargs): Function Body ``` `*args`:允許傳入可變動position參數,儲存資料型態為 tuple。 `**kwargs`:允許傳入可變動keyword參數,儲存資料型態為 dictionary > 若要同時使用則 `*args` 需要在 `**kwargs` 的左邊 ```python= def fun(arg, *args, **kwargs): print(f'arg = {arg}') print(f'args = {args}') print(f'kwargs = {kwargs}') # 執行函數 fun(1, 2, 3, k1=4, k2=5) # 觀察輸出結果 Output: arg = 1 # Position Argument args = (2, 3) # Mutable Position Argument, which type is Tuple kwargs = {'k1': 4, 'k2': 5} # Mutable Keyword Argument, which type is dictionary ``` 對於`*args、**kwargs`該如何運用? 你只需要記得他們的型別為何,就可以運用自如 ```python= # *args mutable position arguments def fun(*args): sum = 0 for num in args: sum += num print(f'sum is {sum}') # Try & Test(T2) # 這樣的寫法非常蠢,想想看有沒有更簡短的寫法 fun(1,2,3,4,5,6,7,8,9,10) # 如果我只想加1~10的奇數就好,該怎麼寫 Output: sum is 55 ``` ```python= # **kwargs mutable keyword arguments def fun(**kwargs): for key, value in kwargs.items(): print(f'({key}, {value})') fun(k1='one', k2='two', k3='six') Output: (k1, one) (k2, two) (k6, six) ``` 傳遞參數時可以使用`*args、**kwargs` ```python= def fun(a1, a2, a3): print(f'a1={a1}, a2={a2}, a3={a3}') # 設定變數 nums = (1,2,3) # Try & Test 將Tuple改成List(T3) kwnums = {'a3' : 6, 'a1' : 4, 'a2' : 5} # 傳遞給函式,這樣的寫法有一種將它展開的感覺 fun(*nums) fun(**kwnums) Output: a1=1, a2=2, a3=3 # fun(*nums) result a1=4, a2=5, a3=6 # fun(**kwnums) result ``` 上面的例子唯一要注意的是,`kwnums`中的key必須為**字串** ### Lambda Function > 這招必學,學會程式碼可以非常簡短!! #### Lambda Syntax ```python= lambda [arg1 [, arg2 [,..., argN]...]] : Lambda Body ``` 關於Lambda的一些重點: 1. Lambda 為一匿名函數 2. Lambda 的參數可以有或沒有 3. Lambda Body only have the exact one expression 4. Lambda Body不需要寫Return - 事實上,可以看成`lambda args : return Lambda Body` - 一樣的概念,如果你的Body像是print(),就是回傳None 5. Lambda Body不能做Assign - ?? 目前不清楚為什麼,剛好寫程式有碰到 - 我想應該`z = x+y`是屬於statement ```python= # Simple example # Normal function def add(x, y=9): return x+y # Lambda function add = lambda x, y=9 : x+y ``` 繼續之前,釐清觀念會更好(內含我自己的推測,可能有錯): > 還記得之前說過,變數為一物件的參考(在Fundamental Concept 第三點)。 > 經過我的觀察,函數也可以用相同的概念來看。 > ```python= # 先進到Python Interpreter def f(): return 1 # type f <function f at 0x00000217D65C52F0> # 網路上沒找相關資訊但我推測後面的數字應該會是記憶體位置 # 我是由 0x + 碼數16位 + python interpreter 64 bits 判斷 # 可以看到函數的行為是被存在一個固定的地方 # f 只是一個變數,它參考到這塊記憶體位址 # 進行以下測試 g = f f = 0 # type g <function f at 0x00000217D65C52F0> # type f 0 # type g() 1 # 其實可以看到我們的function name是甚麼根本沒差 # 重要的是只要參考到我們要的記憶體位址(或是說函式物件),就夠了 # 額外補充()在此用作 function call operator ``` :::info 結論: funcion name如同變數一樣,它們都是一個名稱,都可以參考到一個物件, 只是funcion name一開始被定義時,稍微特別一點,它參考到的物件可以執行`()` ::: 進入主角 ```python= # 先進入Python Interpreter # 這個函式行為跟上面是一樣的,若沒有感覺,趕快回去再看一遍 # type lambda : 1 <function <lambda> at 0x00000217D65C5488> # 到這裡,你可能已經看出來了 # 當我們輸入 lambda : 1 時 # 事實上就是定義了一個函式的行為,並且已經被存在某個記憶體空間了 # 剛剛說了名字是甚麼根本不重要,重要的是我們需要掌握這一塊記憶體位址,才能執行函數行為 # 因此底下提供兩種做法 # 1. 這個函式只會在"現在"用到,並且只會使用一次,那就直接呼叫吧 (lambda : 1)() # 2. 這個函式"未來"會用到,給它一個變數名稱吧 f = lambda : 1 f() ``` :::info 結論: 就跟你說學會這招可以簡短程式碼了吧。 思考一下如果有參數的lambda,用兩種寫法該怎麼表示。 ::: Lambda Example ```python= # 該筆記的Data Structure中List、Dict # 使用sort時,很常用到Lambda,底下都有範例 # 在此不贅述 seq = [-4, -3, 5, 7, 9] filter_res = list(filter(lambda x : x < 0, seq)) # filter_res = [-4, -3] map_res = list(map(lambda x : x**2, seq)) # map_res = [16, 9, 25, 49, 81] # 未來寫視窗會用到 # 在tkinter中的按鈕,預設command是不能帶參數的 # 如果要帶參數怎麼辦? def btn_handler(arg): pass btn = tkinter.Button(..., command=lambda:btn_handler(x)).pack() # 公布解答 # 1. 帶參數直接呼叫 (lambda x, y, z : x*y*z)(3,6,9) # 2. 存起來日後可以用 f = lambda x, y, z : x*y*z f(3,6,9) ``` ### Try & Test T1: Raise **ValueError**, Message is "too many values to unpack" > 所以如果不知道會回傳幾個參數,就用一個變數去取,型別為Tuple;不然就必須要用剛好的變數去取 T2: 參考寫法,歡迎貢獻更多不一樣的寫法 ```python= fun(1,2,3,4,5,6,7,8,9,10) # 改成 fun(*tuple(range(11))) # 只想加奇數 fun(*[x for x in range(11) if x%2]) ``` T3: 答案是沒有問題 > 按照我的理解,list & tuple的差別應該只是 > list is mutable > tuple is imutable