owned this note
owned this note
Published
Linked with GitHub
---
title: Python 入門教學-程式結構
tags: python_beginner
---
# 程式結構
## 註解與換行
* 註解
```python=
# 單行註解
'''
這
不
是
註
解
但運作的方式像註解
'''
```
* 程式碼換行
```python=
>>> alphabet= 'abcdefg' + \
... 'hijklmn' + \
... 'opqrstuv' + \
... 'wxyzw'
```
## 判斷式
* if, elif, else
```python=
>>> grade = 65
>>> level = ''
>>> if grade >= 80:
>>> level = 'A'
>>> elif grade >= 70:
>>> level = 'B'
>>> elif grade >= 60:
>>> level = 'C'
>>> else:
>>> level = 'F'
```
* 比較運算子
* == 相等
* != 不相等
* < 小於
* <= 小於等於
* \> 大於
* \>= 大於等於
* in 是否為...的其中之一
```python=
>>> classA = ['Sam', 'Neko', 'Chia']
>>> if 'Neko' in classA:
... print('Neko is in class A')
... else:
... print('Neko is not in class A')
```
* 布林運算子
* and
* or
* not
```python=
>>> if 'Sam' in classA and not 'Sean' in classA:
... print('Sam is in class A, and Sean is not.')
... else:
... print('Sam is not in classA, or Sean is in class A.')
```
* What is False?
* False
* None
* 0
* 0.0
* ''
* []
* ()
* {}
* set()
* 其他都是True
## 迴圈
* while
```python=
>>> count = 1
>>> while count <= 5:
... print(count)
... count += 1
```
* for
* iterable: 可迭代,list, dictionary, set, tuple, string...
檢查是否為iterable
```python=
>>> from collections import Iterable
>>> isinstance((), Iterable)
True
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance(100, Iterable)
False
```
> 參考: [迭代器 (Iterator)](http://wiki.jikexueyuan.com/project/explore-python/Advanced-Features/iterator.html)
* list
```python=
>>> # while寫法
>>> cats = ['Vincent', 'Peter', 'Fluffy']
>>> current = 0
>>> while current < len(cats):
... print(cats[current])
... current += 1
>>> # for寫法
>>> for cat in cats:
... print(cat)
```
* string
```python=
>>> # 也可以印出字串中的每個字
>>> name = 'Jimmy Page'
>>> for letter in name:
... print(letter)
```
* dictionary
會回傳key
```python=
for food in foods:
print(food)
dict1 = {
'classA':['Sam', 'John', 'David'],
'classB':['Jack', 'Peter', 'Tom'],
'classC':['Sean', 'Victor']
}
for names in dict1.values():
for name in names:
print(name)
```
使用values()回傳值
```python=
>>> for ingredient in foods.values():
... print(ingredient)
```
使用items()回傳key, value的tuple
```python=
>>> for item in foods.items():
... print(item)
```
將tuple值指派給變數
```python=
for food, ingredient in foods.items():
print('Food:', food)
print('Ingredient:', ingredient)
print('---------------')
dict1 = {
'classA':['Sam', 'John', 'David'],
'classB':['Jack', 'Peter', 'Tom'],
'classC':['Sean', 'Victor']
}
for myClass, names in dict1.items():
print("myClass:", myClass)
print("names:", names)
print("--------")
# 再把每個人給印出來呢?
```
* break
break可用來中斷迴圈
* while
```python=
>>> count = 0
>>> while True:
... if count >= 5:
... break
... print(count)
... count += 1
```
* for
```python=
>>> for food in foods:
... if food == 'Chocolate Cake':
... break
... print(food)
```
* continue
continue可以跳過這次迴圈
```python=
>>> count = 0
>>> while count < 10:
... if count % 2 == 0:
... continue
... print(count)
```
* while-else, for-else
假設沒有break掉,就會執行else裡面的東西
```python=
>>> for food in foods:
... if food == 'Spaghetti':
... break
... print(food)
... else:
... print('Spaghetti not found.')
```
* zip()
用zip()可以同時迭代多個序列
```python=
days = ['Monday', 'Tuesday', 'Wednesday']
courses = ['Math', 'English', 'Music']
classrooms = ['A', 'B', 'C', 'D']
for day, course, classroom in zip(days, courses, classrooms):
print("{} : Take a {} course, in the classroom {}".format(day, course, classroom))
```
* range()
range()會回傳可迭代的數列,語法為`range(start, end, step)`
```python=
print(list(range(0, 5)))
for i in range(0, 5):
print(i)
print(list(range(4, 10, 2)))
for i in range(4, 10, 2):
print(i)
```
:::warning
我們也可以定義自己的iterator物件,例如這個Fibonocci數列的iterator
```python=
from collections import Iterator
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1
def __iter__(self):
return self
def __next__(self):
self.a, self.b = self.b, self.a + self.b
return self.a
def main():
fib = Fib()
print('isinstance(fib, Iterator): ', isinstance(fib, Iterator))
for i in fib:
if i > 10:
break
print(i)
main()
```
:::
## 生成式
* 串列生成式
一開始我們建立串列的方式
```python=
num_list = []
num_list.append(1)
num_list.append(2)
num_list.append(3)
num_list.append(4)
num_list.append(5)
print(num_list)
```
使用for來建立串列
```python=
num_list = []
for num in range(1, 6):
num_list.append(num)
print(num_list)
```
使用生成式建立串列
`[運算式 for 項目 in 可迭代的東西]`
```python=
num_list = [num for num in range(1, 6)]
print(num_list)
```
`[運算式 for 項目 in 可迭代的東西 if 條件式]`
```python=
num_list = [num for num in range(1, 10) if num % 2 == 0]
print(num_list)
```
也可以用生成式來建立雙重迴圈才能建立的東西
```python=
# 使用雙重迴圈建立3*3矩陣
cells = []
for row in range(1, 4):
for col in range(1, 4):
cells.append([row, col])
print(cells)
# 使用生成式建立3*3矩陣
cells = [[row, col] for row in range(1, 4) for col in range(1, 4)]
print(cells)
```
Python要建立二維串列要使用生成式
```python=
# 不帶初始值
two_dim_list = [[] for i in range(3)]
# 帶初始值
two_dim_list = [[0] for i in range(3)]
# 決定內部串列的項目數量
two_dim_list = [[0 for j in range(3)] for i in range(3)]
```
:::info
既然講到二維串列,就一定要提一下深複製根淺複製
* **淺複製**
前面我們提到,python複製串列不能用等於,要使用copy()。我們來複製一個二維串列試試看
```python=
two_dim_list = [[0 for j in range(3)] for i in range(3)]
copy_list = two_dim_list.copy()
```
乍看之下沒甚麼問題,但是:
```python=
two_dim_list[0][0] = 2
print(two_dim_list)
print(copy_list)
```
神奇的事情發生了,我們只改了原本的串列中的第一個串列的第一個元素,但複製的那個也跟著改了。
這是因為copy()在複製的時候,python只是將最外層的串列指派新的一個記憶體空間,但是裡面的元素其實是引用到同一個地方的,看看下面的例子就知道了
```python=
print(two_dim_list is copy_list)
print(two_dim_list[0] is copy_list[0])
```
當改動串列中的串列時,因為是同一個串列,所以複製的那個串列也會變動
* **深複製**
要解決這個問題就要使用deepcopy(),深複製的函式
我們使用help()來看看有沒有這個東西
```python=
>>> help(list())
```
沒有看到deepcopy(),這代表list本身沒有內建深複製的函式。
要使用的話就要從別的地方引入進來,方法如下,之後會再細講
```python=
from copy import deepcopy
```
我們做一樣的事情觀察看看
```python=
from copy import deepcopy
two_dim_list = [[0 for j in range(3)] for i in range(3)]
copy_list = deepcopy(two_dim_list)
two_dim_list[0][0] = 2
print(two_dim_list)
print(copy_list)
print(two_dim_list is copy_list)
print(two_dim_list[0] is copy_list[0])
```
:::
## 函式
* 什麼是函式
假設有一個你很常使用的功能,你不想要每次用這個功能時都重新打一遍這個功能,那就可以把這個功能包成一個函式。函式的結構如下
* 參數(Input)
* 動作
* 回傳(Output)
```python=
# 無回傳函式
def print_two_num(num1, num2):
print(num1, num2)
# 有回傳的函式
def connect_two_num(num1, num2):
return str(num1) + str(num2)
# 呼叫函式
print_two_num(1, 2) # 函式沒有回傳值,所以直接呼叫即可
connected = connect_two_num(1, 2) # 函式有回傳值,所以要宣告一個變數來把值存起來
```
函數不只可以回傳字串,也可以回傳list, tuple, set, dictionary等等,所以如果你覺得創建一個二微串列很麻煩,你也可以做一個函式來創建二維串列
```python=
def create_2d_list(num1, num2):
return [[0 for j in range(num2)] for i in range(num1)]
list_2d = create_2d_list(3, 5)
```
為你的函式加上說明,讓別人(還有你自己)知道這個函式的功能
```python=
def create_2d_list(num1, num2):
'''
Create and return an 2 dimension list.
First number is height, and second number is width.
'''
return [[0 for j in range(num2)] for i in range(num1)]
# 查看說明
help(create_2d_list)
# 只查看我們自己寫的文件字串
print(create_2d_list.__doc__) # __什麼什麼__的這種東西以後我們再介紹
```
* 命名空間(namespace)與範圍
為了不要互相影響,只會在自己所在的命名空間中作用,不同命名空間的變數就算名稱相同也不會互相影響。命名空間的範圍分為兩種:
* global(全域): 在函式外面命名的變數都屬於全域的命名空間,不論在這個程式中的哪個地方都可以取用,我們把這些變數稱為全域變數(global variable)
* local(區域): 在函式內部定義的變數只能在這個函式中使用,我們稱作區域變數(local variable)
使用`locals()`以及`globals()`這兩個函式可以查看目前有哪些變數是local的,那些是global的
```python=
global_name = "Jack"
print("global:", globals())
```
```python=
def i_am_local():
local_name = "Sam"
print("locals:", locals())
```
在函式當中可以直接取得全域變數的值,注意下面的例子,函數並沒有接收到任何的輸入,而是直接使用全域變數
```python=
global_name = "Jack"
def print_name():
print(global_name)
```
但是如果要在函式內對全域變數進行更改,就必須加上變數前面加上global
```python=
# 這樣等同於在區域變數當中再開一個同名變數,但其實外面的變數根本沒有改變
global_name = "Jack"
def error_global():
global_name = "HaHaHa" # 在函式中宣告一個新的global_name
print(global_name)
print(global_name) # 外面的global_name並沒有改變
#這樣才是對的,至始至終都是使用全域變數
global_name = "Jack"
def correct_global():
global global_name
global_name = "HaHaHa"
print(global_name)
print(global_name)
```
* 參數
Python傳遞參數的方式有很多種
* 位置參數: 依照位置來指定參數,這個位置放甚麼變數,函數就接收甚麼
```python=
def print_num(first, second, third):
print(first, second, third)
print_num(1, 2, 3)
```
* 關鍵字參數: 依照名字來指定這個變數要傳給哪個參數
```python=
def print_num(first, second, third):
print(first, second, third)
print_num(second=2, third=3, first=1)
```
* 預設參數: 參數可以給預設值,如果呼叫時沒有傳這個參數,就會使用他的預設值
```python=
def diner(appetizer, main_course, dessert='pudding'):
print("Appetizer:", appetizer)
print("Main course:", main_course)
print("Dessert:", dessert)
diner("soup", "chicken")
diner("soup", "chicken", "chocolate icecream")
```
:::info
python不支援多載(overloading),但是透過預設參數,可以達到跟多載一樣的效果。
去搜尋一下多載什麼意思,關鍵字:
* function overloading
* 函式多載
* 函式重載
:::
* 用\*收集位置參數: 假設你不能確認總共有多少參數會傳進來,也可以使用\*來定義你的函數,記得要寫在最後面
```python=
def print_num(first, second, *rest):
print("first:", first)
print("second:", second)
print("All the rest:", rest)
print_num(1, 2, 3, 4, 5, 6, 7, 8)
```
\*rest會接收剩下的參數,用tuple存起來
:::info
使用慣例上,會用args來當作這個參數的名稱
```python=
def print_num(first, second, *args):
print("first:", first)
print("second:" second)
print("All the rest:", rest)
```
:::
* 用\*\*收集關鍵字參數: \*\*可以用來收集不確定數量的關鍵字參數
```python=
def print_foods(**foods):
print(foods)
print_foods(appetizer='salad', main_course='steak', dessert='pudding')
```
python會把這些值用dictionary存起來
:::info
慣例上使用\*\*kwargs來命名
```python=
def print_foods(**kwargs):
print(kwargs)
```
:::
* 把函數當成參數來傳遞
:::warning
這個特性只有在那些號稱"everything is object"的程式語言當中才有,例如Python以及JavaScript。
其他語言,像是C/C++、JAVA是沒有的
:::
在python當中,所有東西都是物件(everything is object),函數也是。你可以把函數指派給變數,也可以回傳一個函數,當然也可以把函數當成參數來傳遞
```python=
def add_args(*args):
result = 0
for n in args:
result += n
return result
def mul_args(*args):
result = 1
for n in args:
result *= n
return result
def run_something_with_args(func, *args):
return func(*args)
add_this = run_something_with_args(add_args, 1, 2, 3, 4, 5)
mul_this = run_something_with_args(mul_args, 1, 2, 3, 4, 5)
```
:::info
注意,我們前面在呼叫函數的時候,都有加上括號,例如`add_num(1, 2)`,但仔細看,在定義run_something_with_args時、還有呼叫它的時候,我們傳遞進去的只有函數的名稱,而沒有加上括號。
函數加上括號代表的是呼叫這個函數,也就是使用這個函數。
而沒有上括號,代表我們目前是把函數視為一個名為function的物件。既然是物件,那就可以拿來傳來傳去當然沒問題。
既然是物件,那當然可以用`type(add_args)`來看看add_args的type是甚麼東西。也可以用`id(add_args)`來看看這個函式在什麼記憶體位置。
這沒那麼好懂,想一下
:::
* lambda(): 匿名函式
假設要寫的函式很短,也可以用lambda來定義一個函式
語法: `lambda arg1, arg2: expression`
```python
# 原本的函式
def add(a, b):
return a + b
result = add(2, 5)
print(result)
# 使用lambda
add = lambda a, b: a + b
result = add(2, 3)
print(result)
```
## 作業1 - 終極密碼
* 要求
1. 請於程式第一行輸出電腦亂數出來的數字,範圍1~100,亂數產生的程式碼如下
```python=
import random # 引入random套件,來產生亂數
target = random.randint(1, 100) # randint(a, b),隨機產生整數,範圍為 a <= x <= b
print(target)
```
2. 每次輸入前,輸出一個提示,告訴使用者目前可能範圍為多少到多少之間
3. 如果使用者輸入的數字大於目標數字,則輸出"太大囉",反之輸出"太小囉"
4. 使用者答對了之後要輸出"恭喜答對",並且輸出使用者總共猜了幾次
* 範例

## 作業2 - 請利用函數設計一個Text Adventure RPG
Text Adventure RPG(Role Play Game)文字冒險角色扮演遊戲,是一種很古老的遊戲。在電腦還只有黑底白字的CLI時代,人們玩遊戲的方式只能透過文字去想像遊戲的場景,並透過各種設定好的選項來進行動作。如果你完全沒概念的話,請參考"勇者鬥阿德"。
請你利用不同的函式代表各個關卡,角色需要通過這些關卡才能通關遊戲,請提供至少兩種結局(good end, dead end)。內容自行設計,規模可大可小,重點是要熟悉函數的使用。
使用者輸入時請以`> `作為提示符號,以區分使用者輸入與遊戲輸出文字,例如:
```python=
print("一隻龐大的甜甜圈正齜牙裂嘴的朝你大吼(你覺得這很可笑),請問你要逃跑還是攻擊?")
while True:
move = input("> ")
if move == "逃跑":
print("雖然不知為何要逃離這可笑的東西,但你還是逃跑了")
break;
elif move == "攻擊":
print("你看起來想餓了十天一樣,朝甜甜圈咬去")
print("甜甜圈受到激烈的攻擊,倒下")
break;
else:
print("輸入錯誤,請重新輸入")
```
這邊先幫你定義好開始的函數,以及死亡的函數,這只是預設內容,可自行改掉。
```python=
def in_the_room():
# 這個關卡你要設計的內容
def dead(why):
print("你死於「%s」" % why)
def start():
print("某天清晨,你帶著迷迷糊糊的眼神從床上起來。")
print("發現一塊餅乾正衝著你微微一笑。")
print("請問你決定繼續睡覺還是起床?")
move = input("> ")
while true:
if move == "繼續睡覺":
print("在你睡覺的時候,餅乾大軍來襲,合力把你壓死了。")
dead("被餅乾壓死")
elif move == "起床":
in_the_room() # 呼叫第一個關卡
else:
print("輸入錯誤,請重新輸入")
start() # 記得呼叫你的start函數,以開始遊戲
```