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