---
tags: csc
---
# PYTHON and Discord Bot Tutorial
## PYTHON 基礎語法
### Prior Knowledge
- `PYTHON` 的執行是直譯器(依照程式碼一行一行依序執行)。
- 執行環境:`IDLE`、`Anaconda` 等。
### Beginning
```python=
print('hello, world')
```
`print()` 是輸出函數,
`'hello, world'` 是一字串型態的參數。
```python=
print(100)
```
`100` 是一整數型態的參數。
### Type and Variable
```python=
print(type('hello, world')) # <class 'str'>
print(type(123)) # <class 'int'>
```
#### 變數型態
- `int` : `integer` 整數型態。
- `str` : `string` 字串型態,以 `""` 或是 `''` 包圍字串。
- `float` : 小數型態(浮點數型態)。
- `bool` : `boolean` 布林值型態,`True` 代表 `1`,`False` 代表 `0`。
- `NoneType` :沒有型態。
- `complex` :複數型態,以 `A+Bj` 表示。
#### 算術運算子
- `+` 加法運算子: `int`, `str`, `float`, `bool`, `complex` 。
- `−` 減法運算子: `int`, `float`, `bool`, `complex`。
- `∗` 乘法運算子: `int`, `str`, `float`, `bool`, `complex`。
- `/` 除法運算子: `int`, `float`, `bool`, `complex`。
- `//` 整數除法運算子: `int`, `float`, `bool`, `complex`。
- `%` 餘數運算子: `int`, `float`, `bool`。
- `∗∗` 指數運算子: `int`, `float`, `bool`, `complex`。
#### 用法
1. 字串形態的 `+` 加法運算僅限於兩字串形態相加,若「字串 `+` 整數」或「整數 `+` 字串」(整數可替換為其它形態),會發生執行錯誤。
```python=
print('hello, world' + 100)
# TypeError: can only concatenate str (not "int") to str
```
2. 字串形態的 `∗` 乘法運算僅限於「字串 `∗` 整數」或是「整數 `∗` 字串」(只能整數)。
```python=
print('hello, world' * 2) # 意同於 print(2 * 'hello, world')
# hello, worldhello, world
```
```python=
print('hello, world' * 2.5)
# TypeError: can't multiply sequence by non-int of type 'float'
```
```python=
print('hello, world' * 'hello, world')
# TypeError: can't multiply sequence by non-int of type 'str'
```
#### 型態轉換
```python=
int('100')
# 從字串轉為整數型態
```
```python=
str(2.5)
# 從浮點數轉為字串型態
```
#### 指定變數
```python=
n = 100
# 設定整數變數 n,並賦值為 100
```
```python=
s = 'hello, world'
# 設定字串變數 s,並賦值為 'hello, world'
```
- 變數名稱不可數字開頭。
- 變數名稱可以用全形中文字命名(`PYTHON` 可以,其它語言大部分不行)。
#### 讀入變數
```python=
s = input()
```
- 讀入為字串形態。
- 讀入為一整行,不會因讀到空白而停止。
#### 算術、指定運算子結合
- n = n + 2 `⟶` n += 2
- n = n − 2 `⟶` n −= 2
- n = n ∗ 2 `⟶` n ∗= 2
- n = n / 2 `⟶` n /= 2
- n = n // 2 `⟶` n //= 2
- n = n % 2 `⟶` n %= 2
#### 輸出
```python=
print(value, ..., sep = ' ', end = '\n')
```
- 傳入多個參數,以逗號隔開。
- 間隔預設是一個空白鍵,可以透過更改 `sep` 參數改變。
- 結尾預設是一個換行字元,可以透過更改 `end` 參數改變。
```python=
print(1, 2, 3) # 1 2 3
print(1, 2, 3, sep = '_') # 1_2_3
print(1, 2, 3, end = '...') # 1 2 3...
```
### If
#### 關係運算子
- `>` : 大於。
- `>=` : 大於等於。
- `<` : 小於。
- `<=` : 小於等於。
- `==` : 等於。
- `!=` : 不等於。
關係運算回傳值為布林值的 True 或 False 。
```python=
ans = (1 != 2)
print(ans) # True
```
```python=
ans = (1 > 2)
print(ans) # False
```
#### 邏輯運算
- and
| | True | False |
| --------- | ---------- | ----------- |
| **True** | $_{True}$ | $_{False}$ |
| **False** | $_{False}$| $_{False}$ |
- or
| | True | False |
| --------- | ---------- | ----------- |
| **True** | $_{True}$ | $_{True}$ |
| **False** | $_{True}$ | $_{False}$ |
- not
| | True | False |
| --------- | ---------- | ----------- |
| **True** | $_{False}$ | $_{True}$ |
#### if 用法
```python=
if expression:
statements
```
- 如果 `expression` 是 `True` 就執行以下 `statements`。
- 待執行內容需要縮排(`空四格` 或是 `Tab`,不可混著使用)。
#### elif 和 else 用法
```python=
if expression1:
statements1
elif expression2:
statements2
elif expression3:
statements3
else:
statements4
```
- `elif` 是 `else` 和 `if` 的簡寫,意思是如果還有條件是否為 `True`。
- `else` 是所有條件皆不成立,才會執行。
- 只要其中一個 `expression` 成立,執行完其 `statements`,即會跳出其它的 `expression`。
#### 用法
```python=
a = 1
b = 2
if a > b:
maximum = a
minimum = b
else:
maximum = b
minimum = a
print('maximum:', maximum)
print('minimum:', minimum)
```
```python=
n = -1
if n > 0:
print('positive')
elif n < 0:
print('negative')
else:
print('zero')
```
```python=
year = 2000
if year < 0:
print('illegal')
elif year % 4 == 0 and year % 100 != 0 or year % 400 == 0:
print('leap year')
else:
print('normal year')
```
### Loop
`loop` 稱為迴圈,分為兩種:
1. `while loop`
2. `for loop`
#### while loop
```python=
while expression:
statements
# update conditions
```
當`expression` 成立時,
就一直重複執行縮排內的 `statements`,
直到 `expression` 為 `False` 時即停止。
#### 用法
```python=
# 計算 0 ~ 10 總和
i = 0
sum = 0
while i <= 10:
sum += i
i += 1
print(sum) # 55
```
```python=
# 輾轉相除法
a, b = 18, 24
while b != 0:
a, b = b, a % b
print(a) # 6
```
#### for loop
```python=
for i in iterable:
statements
```
用一個變數 `i` 迭代一個可迭代的物件。
常見可迭代物件:
- `range` : `range(start, stop, step)`
```python=
for i in range(3):
print(i)
'''
0
1
2
'''
```
```python=
for i in range(3, 6):
print(i)
'''
3
4
5
'''
```
```python=
for i in range(5, 10, 3):
print(i)
'''
5
8
'''
```
- map
`map` : `map(function, iterable)` 回傳一個迭代器 (`iterator`),使用 `iterable` 的每個元素來計算函數。原文:(make an iterator that computes the function using arguments from each of iterables.)。
```python=
for i in map(lambda x: x ** 2, [1, 2, 3]): # 對於 [1, 2, 3] 每個元素回傳 x ** 2
print(i)
'''
1
4
9
'''
```
- `filter` : `filter(func or None, iterabe)`,返回一個迭代器,產生 `iterable` 中 `function(item)` 為 `True` 的項目,如果函數為 `None`,則返回為 `True` 的項目。(原文:return an iterator yielding those iems of iterable for which fucntion(item) is True. If function is None, return the item that are True)。
```python=
for i in filter(lambda x: x % 2 == 0, [1, 2, 3, 4]): # 過濾, 只取 x%2==0 的元素
print(i)
'''
2
4
'''
```
```python=
for i in filter(None, [0, 1, 0, 2, 0]): # 過濾, 只取為 True(不為 0) 的元素
print(i)
'''
1
2
'''
```
- `str` : 字串。
- `list` : 串列。
- `tuple` 。
- `dict` : 字典。
#### 用法
```python=
# 九九乘法表
for i in range(1, 10):
for j in range(2, 10):
print(j, '*', i, '=', j * i, end = '\t')
print()
```
### Funtion
在了解 `function` 之前,
其實我們已經先用很多 `function` 了。
- `print()`
- `input()`
- `int()`,` str()`, `float()` 等。
- `sum()`, `min()`, `max()`, `len()` 等。
其實就是有「函數名稱」、「括號(傳入參數)」,
做特定的指令並回傳數值。
然而,
還有好多好用的內建 `function` 可以使用。
- `abs(value)` : 回傳 `value` 的絕對值。
```python=
print(abs(-2.5)) # 2.5
```
- `bin(interger)` : 回傳 `interger` 的二進位表示值。
```python=
print(bin(100)) # 0b1100100
```
- `divmod(x, y)` : 回傳 `tuple(x // y, x % y)`。
```python=
print(divmod(25, 4)) # (6, 1)
```
- `eval(string)` : 評估給定的 `string`,對於 `string` 做給定的指令。(原文:evaluate the given source int the context of globlas and locals.)。
```python=
print(eval('1 * 2 + 3 - 4 // 2')) # 3
```
```python=
eval('print(100)') # 100
```
```python=
print(eval('len([1, 2, 3, 4, 5])')) # 5
```
- `oct(interger)` : 回傳 `interger` 代表的八進位表示值。
```python=
print(oct(100)) # 0o144
```
- `pow(base, exp, mod)` : 回傳 $base^{exp} \% mod$。若沒有傳入 `mod` 參數,則回傳 $base^{exp}$。
```python=
print(pow(2, 10, 5)) # 4
```
```python=
print(pow(2, 10)) # 1024
```
- `sorted(iterable, key = None, reverse = False)` : 回傳一個排序好的 `iterable` 物件。可依照喜好的 `key` 和是否要 `reverse` 排序。
```python=
print(sorted([5, 1, 3, 2, 4])) # [1, 2, 3, 4, 5]
```
```python=
print(sorted([5, 1, 3, 2, 4], key = lambda z: (z % 3, z))) # [3, 1, 4, 2, 5]
```
```python=
print(sorted([5, 1, 3, 2, 4], reverse = True)) # [5, 4, 3, 2, 1]
```
我們也可以自己寫 `function`。
#### 用法
```python=
def add(a, b): # a, b 為傳入參數
return a + b # 回傳值
ans = add(1, 2) # 使用 function
print(ans) # 3
```
#### 預設參數(Default Argument)
```python=
def add(a = 10, b = 20):
'''
預設參數 a = 10, b = 20
若沒有傳入參數就用預設參數代替
'''
return a + b # 回傳值
print(add(1, 2)) # 3
print(add(1)) # 21
print(add()) # 30
print(add(b = 0)) # 10
```
- 中間有預設引數但後面沒有會發生錯誤。
```python=
def add(a = 10, b, c = 20):
return a + b + c # 回傳值
# SyntaxError: non-default argument follows default argument
```
`function` 看似多了個步驟,
用起來還要注意細節,
十分麻煩。
但其實 `function` 對於重複性質高程式碼,
可帶來很大的便利。
```python=
def isprime(n):
if n == 2:
return True
if n % 2 == 0 or n < 2:
return False
for i in range(2, int(n ** 0.5) + 1):
if n % i == 0:
return False
return True
print(isprime(2)) # True
print(isprime(10)) # False
```
還有「匿名函數(`lambda function`)」可以用。
冒號前面是參數,
冒號後面是回傳值。
```python=
mul = lambda a, b: a * b
print(mul(10, 20)) # 200
```
對於只需使用一次的函數,
通常會用匿名函數表示。
```python=
# 使用 normal function
def f(z):
return (z % 3, z) # 根據 (z % 3, z) 排序
print(sorted([5, 1, 3, 2, 4], key = f)) # [3, 1, 4, 2, 5]
```
```python=
# 使用 lambda fuction
print(sorted([5, 1, 3, 2, 4], key = lambda z: (z % 3, z))) # [3, 1, 4, 2, 5]
```
### Import
`import` `module`,
顧名思義就是導入模組,
由於簡化程式碼長度,
可以導入已經寫好的模組使用。
導入方式(以 `math` 為例) :
```python=
import math # 直接導入, 之後使用前要加 math
```
```python=
import math as m # 導入名稱以 m 代入, 之後使用前要加 m
```
```python=
from math import * # 導入 math 所有東西, 之後直接使用
```
- `datetime` : 處理基本的日期和時間類型處理基本的日期和時間類型
```python=
import datetime
# datetime.date(year, month, day)
x = datetime.date(2020, 1, 1) # 2020 年 1 月 1 日
y = datetime.date(2020, 10, 10) # 2020 年 10 月 10 日
print((y - x).days) # 時間差幾天 # 283
print((y - x).seconds) # 0 # 沒有指定 second, 無法計算
print('----------')
# datetime.datetime(year, month, day, hour, minute, second)
x = datetime.datetime(2020, 1, 1, 1, 1, 1)
y = datetime.datetime(2020, 10, 10, 10, 10, 10)
print((y - x).days) # 時間差幾天 # 283
print((y - x).seconds) # 時間差幾秒 # 32949
```
- `itertools` : 為高效循環而創建迭代器的函數。
```python=
import itertools
lst = [1, 2, 3]
# 排列
for i in itertools.permutations(lst, 2): # P 3 取 2
print(i)
'''
(1, 2)
(1, 3)
(2, 1)
(2, 3)
(3, 1)
(3, 2)
'''
print('----------')
# 組合
for i in itertools.combinations(lst, 2): # C 3 取 2
print(i)
'''
(1, 2)
(1, 3)
(2, 3)
'''
print('----------')
# 笛卡爾積
for i in itertools.product(lst, repeat = 2): # list 相乘, 重複 2 次
print(i)
'''
(1, 1)
(1, 2)
(1, 3)
(2, 1)
(2, 2)
(2, 3)
(3, 1)
(3, 2)
(3, 3)
'''
```
- `math` : 數學函數
```python=
import math
# 常數
print(math.pi) # 3.141592653589793
print(math.e) # 2.718281828459045
print('----------')
# 三角函數(弧度量)
pi = math.pi
print(math.sin(pi)) # 1.2246467991473532e-16
print(math.cos(pi)) # -1.0
print(math.tan(pi)) # -1.2246467991473532e-16
print('----------')
# 反三角函數(弧度量)
print(math.acos(-1)) # 3.141592653589793
print(math.asin(-1)) # -1.5707963267948966
print(math.atan(-1)) # -0.7853981633974483
print('----------')
# 捨去, 進位
print(math.ceil(2.4)) # 向上取整 # 3
print(math.floor(2.4)) # 向下取整 # 2
print('----------')
# 階乘
print(math.factorial(3)) # 6
print(math.factorial(10)) # 3628800
print('----------')
# 最大公因數
print(math.gcd(18, 24)) # 6
print('----------')
# 排列, 組合
print(math.perm(5, 2)) # P 5 取 2 # 20
print(math.comb(5, 2)) # C 5 取 2 # 10
print('----------')
# 對數運算
print(math.log10(100)) # 2.0
print(math.log2(16)) # 4.0
print(math.log(10)) # 以 e 為底 # 2.302585092994046
print(math.log(27, 3)) # 以 3 為底 # 3.0
```
- `random` : 生成隨機數。
```python=
import random
lst = [1, 2, 3, 4, 5]
print(random.choice(lst)) # 從 lst 隨機選取
print('----------')
print(random.choices(lst, k = 2)) # 從 lst 隨機選取 2 個, 回傳一個 list
print('----------')
print(random.randint(1, 10)) # 從 [1, 10] 隨機選取
print('----------')
print(random.randrange(1, 10, 3)) # range(1, 10, 3) 隨機選取
print('----------')
print(random.random()) # 從 [0, 1) 隨機選取小數
print('----------')
random.shuffle(lst) # 打亂 lst
print(lst)
```
事實上,
還有很多模組要透過網路下載,
有些 `OJ` 並不能使用,
而這些模組就是 `PYTHON` 的精華所在。
## PYTHON List
### Assign
```python=
# 指定空 list
lst = []
lst = list()
```
```python=
# 指定有元素的 list
# 型態可不一樣
lst = [1, '1', 1.0]
lst = [i for i in range(5)] # [0, 1, 2, 3, 4]
lst = [0] * 5 # [0, 0, 0, 0, 0] # 不可這樣:lst = [[]] * 5, 址會一樣
lst = [int(x) for x in input().split()]
```
### Index and Value
#### index 索引值
- 主要作用為取值和修改。
- 範圍 `1` ~ `len(list) - 1`,大於 `len(list) - 1` 會發生錯誤。
- 範圍 `-1` ~ `-len(list)` , 小於 `-len(list)` 會發生錯誤。
#### value 值
- 為 `index` 所對應到的值。
- 可為任意型態。
#### 用法
```python=
lst = [2, '4', 6.0]
print(lst[0]) # index: 0, value: 2
print(lst[1]) # index: 1, value: '4'
print(lst[2]) # index: 2, value: 6.0
```
### Slice
- 取片段 `list`。
- `lst[start: stop: step]`,`stop` 預設是 `len(list)`,`step` 預設是 `1`。
#### 用法
```python=
lst = [1, 2, 3, 4, 5]
print(lst[3:]) # [4, 5] # 從 index: 3 開始到結束
print(lst[: 3]) # [1, 2, 3] # 從頭開始到 index: 3 結束
print(lst[:: 2]) # [1, 3, 5] # 從頭到尾, 間隔為 2
```
### Method
- `append(object)` : 將 `object` 加到 `list` 的末尾。
```python=
lst = []
print(lst) # []
lst.append(1)
print(lst) # [1]
lst.append(2)
print(lst) # [1, 2]
lst.append(3)
print(lst) # [1, 2, 3]
```
- `clear()` : 從 `list` 刪除所有項目。
```python=
lst = [1, 2, 3]
lst.clear()
print(lst) # []
```
- `copy()` : 回傳一個淺複製的 `list`。
```python=
# 沒有使用 copy()
lst_1 = [1, 2, 3]
lst_2 = lst_1
lst_2[0] = 100 # 更改元素
print(lst_1) # [100, 2, 3]
print(lst_2) # [100, 2, 3]
```
```python=
# 使用 copy()
lst_1 = [1, 2, 3]
lst_2 = lst_1.copy()
lst_2[0] = 100 # 更改元素
print(lst_1) # [1, 2, 3]
print(lst_2) # [100, 2, 3]
```
- `count(value)` : 回傳 `value` 出現的次數。
```python=
lst = [1, 2, 2, 3, 3, 3]
print(lst.count(1)) # 1
print(lst.count(2)) # 2
print(lst.count(3)) # 3
print(lst.count(4)) # 0
```
- `extend(iterable)` : 透過附加的 `iterable` 的元素來拓展 `list`。
```python=
lst = [1, 2]
lst.extend([3, 4])
print(lst) # [1, 2, 3, 4]
```
- `index(value)` : 回傳 `value` 在 `list` 的第一個 `index`,若 `value` 不在 `list`,則會發生錯誤。
```python=
lst = [1, 2, 4]
print(lst.index(1)) # 0
print(lst.index(2)) # 1
print(lst.index(3)) # ValueError: 3 is not in list
```
- `insert(index, object)` : 插入`object` 在 `index` 前。
```python=
lst = [0, 2, 8]
lst.insert(2, 4)
print(lst) # [0, 2, 4, 8]
lst.insert(3, 6)
print(lst) # [0, 2, 4, 6, 8]
```
- `pop()` : 若無參數,則刪除尾端元素;反之,刪除指定 `index` 的元素。若指定 `index` 超過 `list` 的長度會發生錯誤。對於空的 `list` 使用 `pop()` 亦會發生錯誤。若成功執行,會回傳刪除的元素。
```python=
lst = [1, 2, 3, 4, 5]
print(lst.pop()) # 5
print(lst) # [1, 2, 3, 4]
```
```python=
lst = [1, 2, 3, 4, 5]
print(lst.pop(0)) # 1
print(lst) # [2, 3, 4, 5]
```
- `remove(value)` : 刪除 `value` 在 `list` 第一次出現的值。如果 `value` 不在 `list` 則會發生錯誤。
```python=
lst = [1, 2, 2, 3]
lst.remove(2)
print(lst) # [1, 2, 3]
```
```python=
lst = [1, 2, 3]
lst.remove(4) # ValueError: list.remove(x): x not in list
```
- `reverse(value)` : 刪除 `value` 在 `list` 第一次出現的值。如果 `value` 不在 `list` 則會發生錯誤。
```python=
lst = [1, 2, 2, 3]
lst.remove(2)
print(lst) # [1, 2, 3]
```
```python=
lst = [1, 2, 3]
lst.remove(4) # ValueError: list.remove(x): x not in list
```
- `reverse()` : 將 `list` 反轉。
```python=
lst = [1, 2, 3]
lst.reverse()
print(lst) # [3, 2, 1]
```
- `sort()` : 數字型態不可和字串形態排序。若無參數,則將 `list` 由小到大排序。參數可有 `key`、`reverse`,`key` 可以依照喜好方法排序,`reverse` 預設是 `False`,可以改成 `True`,設為由大到小排序。
```python=
lst = [5, 1, 3, 2, 4]
lst.sort()
print(lst) # [1, 2, 3, 4, 5]
```
```python=
lst = [5, 1, 3, 2, 4]
lst.sort(reverse = True) # 由大到小排序
print(lst) # [5, 4, 3, 2, 1]
```
```python=
lst = [5, 1, 3, 2, 4]
lst.sort(key = lambda z: (z % 3, z)) # 根據 (z % 3, z) 排序
print(lst) # [3, 1, 4, 2, 5]
```
### Function
- `len(list)` : 回傳 `list` 的長度。
```python=
lst = [1, 2, 3, 4, 5]
print(len(lst)) # 5
```
- `max(list)` : 回傳 `list` 的最大值。
```python=
lst = [1, 2, 3, 4, 5]
print(max(lst)) # 5
```
- `min(list)` : 回傳 `list` 的最小值。
```python=
lst = [1, 2, 3, 4, 5]
print(min(lst)) # 1
```
- `sum(list)` : 回傳 `list` 的總和。
```python=
lst = [1, 2, 3, 4, 5]
print(sum(lst)) # 15
```
### Traversal
- 使用 `range()`、`len()`、`index`。
```python=
lst = [1, 2, 3, 4, 5]
for i in range(len(lst)):
print('index:', i, 'value:', lst[i])
'''
index: 0 value: 1
index: 1 value: 2
index: 2 value: 3
index: 3 value: 4
index: 4 value: 5
'''
```
- 直接迭代。
```python=
lst = [1, 2, 3, 4, 5]
for v in lst:
print(v)
'''
1
2
3
4
5
'''
```
- 使用 `enumerate` 以 `index`、`value` 迭代。
```python=
lst = [1, 2, 3, 4, 5]
for i, v in enumerate(lst):
print('index:', i, 'value:', v)
'''
index: 0 value: 1
index: 1 value: 2
index: 2 value: 3
index: 3 value: 4
index: 4 value: 5
'''
```
## PYTHON String
### Assign
- 指定空字串
``` python=
s = ''
s = ""
s = str()
```
- 指定非空字串
```python=
s = 'abc'
s = "abc"
```
### Slice
同 `list`。
### Method
- `s.capitalize()` :回傳第一個字元為大寫的 `s` 字串(不會更改 s)。
```python=
s = 'abc ABC 123'
print(s.capitalize()) # Abc abc 123
```
- `s.upper()` :回傳英文部分全部大寫(其餘部分不變)的 `s` 字串(不會更改 `s`)。
```python=
s = 'abc ABC 123'
print(s.upper()) # ABC ABC 123
```
- `s.lower(`) :回傳英文部分全部小寫(其餘部分不變)的 `s` 字串(不會更改 `s`)。
```python=
s = 'abc ABC 123'
print(s.lower()) # abc abc 123
```
- `s.isupper()` :如果字串 `s` 的英文部分全為大寫則回傳 `True` ;反之 `False` 。
```python=
s = 'ABC 123'
print(s.isupper()) # True
```
- `s.islower()` :如果字串 `s` 的英文全為小寫則回傳 True ;反之 False 。
```python=
s = 'ABC 123'
print(s.islower()) # False
```
- `s.isalpha()` :如果字串 `s` 全由字母組成則回傳 `True`;反之 `False`。
```python=
s = 'abcABC'
print(s.isalpha()) # True
```
- `s.isdigit()` :如果字串 `s` 全由數字組成則回傳 `True`;反之 `False`。
```python=
s = 'abcABC'
print(s.isdigit()) # False
```
- `s.count(sub, start, end)` :回傳 `sub` 在 `s[start: end]` 出現的次數。若沒有傳入 `start`、`end` 參數,則預設為 `0` 、`len(s)` 。
```python=
s = 'abc abc abc'
print(s.count('abc')) # 3
print(s.count('abc', 4)) # 2
print(s.count('abc', 4, 7)) # 1
```
- `s.find(sub, start, end)` :回傳 `sub` 在 `s[start: end]` 第一個出現的位置,若 `sub` 不在 `s` 裡面,則回傳 `−1`。若沒有傳入 `start`、`end` 參數,則預設為 `0`、`len(s)`。
```python=
s = 'abc abc abc'
print(s.find('abc')) # 0
print(s.find('abc', 3)) # 4
print(s.find('abc', 3, 4)) # -1
```
- `s.join(list)` :在 `list` 內每一個字串插入 `s`,並回傳一個新字串。`list` 內的每一個元素型態一定要是 `str`,否則會出現錯誤。
```python=
s = '...'
print(s.join(['a', 'b', 'c'])) # a...b...c
```
- `s.lstrip()` :回傳移除左端不可見字元的字串 `s` (不會更改 `s`)。
```python=
s = ' abc '
print(s.lstrip()) # 'abc '
```
- `s.rstrip()` :回傳移除右端不可見字元的字串 `s`(不會更改 s)。
```python=
s = ' abc '
print(s.rstrip()) # ' abc'
```
- `s.strip()` :回傳移除兩端不可見字元的字串 `s`(不會更改 s)。
```python=
s = ' abc '
print(s.strip()) # 'abc'
```
- `s.replace(sa, sb, times)` :回傳字串 `s` 中 `sa` 取代成 `sb` 的字串,`times` 為次數(不會更改 `s`)。若沒有傳入 `times` 參數,則預設是 `−1`,也就是全部取代。
```python=
s = 'abc abc abc'
print(s.replace('abc', 'ABC')) # ABC ABC ABC
print(s.replace('abc', 'ABC', 1)) # ABC abc abc
```
- `s.split(sep, times)` :使用 `sep` 作為分隔字串,`times` 為分隔次數,並回傳一個 `list`(不會更改 `s`)。若沒有傳入 `sep` 參數,則以空白字元為預設。若沒有傳入 `times` 參數,則預設值是 `−1`,也就是全部取代。
```python=
s = 'abc abc'
print(s.split()) # ['abc', 'abc']
print(s.split(' ')) # ['abc', '', '', 'abc']
print(s.split(' ', 1)) # ['abc', ' abc']
```
### Function
- `len(s)` :回傳 `s` 的長度。
```python=
s = 'abc abc'
print(len(s)) # 7
```
### Traversal
同 `list`。
## PYTHON Dict
### Assign
- 指定空 `dict`
``` python=
# 指定空 dict
d = {}
d = dict()
```
- 指定有內容的 `dict`
```python=
# 指定有內容的 dict
d = {'a': 1, 'b': 2}
# a -> 1, b -> 2
d = {chr(i): i for i in range(65, 91)} # ASCII 表
# A -> 65, B -> 66, C -> 67, ..., Z -> 90
```
### Usage
#### 名詞
- `key` :關鍵字,欲存取對應值的關鍵字。 `key` 必須是 `hashable` (如: `list` 不可當 `key` )。
- `value` :值,關鍵字對應的值。`value` 可為任意型態
#### 存取
```python=
d = {}
d['a'] = 1 # a -> 1
print(d) # {'a': 1}
d['b'] = 2 # b -> 2
print(d) # {'a': 1, 'b': 2}
```
#### 修改
```python=
d = {}
d['a'] = 1 # a -> 1
print(d) # {'a': 1}
d['a'] = 2
print(d) # {'a': 2}
d['a'] += 1
print(d) # {'a': 3}
```
#### 取值
```python=
d = {'a': 1, 'b': 2}
print(d['a']) # 1
print(d['b']) # 2
print(d['c']) # KeyError: 'c'
```
### Method
- `clear()` :從 `dict` 刪除所有項目。
```python=
d = {'a': 1, 'b': 2}
d.clear()
print(d) # {}
```
- `copy()` :回傳一個淺複製的 `dict`。
```python=
# 沒有使用 copy
d1 = {'a': 1, 'b': 2}
d2 = d1
d2['a'] = 10
print(d1) # {'a': 10, 'b': 2}
print(d2) # {'a': 10, 'b': 2}
```
```python=
# 使用 copy
d1 = {'a': 1, 'b': 2}
d2 = d1.copy()
d2['a'] = 10
print(d1) # {'a': 1, 'b': 2}
print(d2) # {'a': 10, 'b': 2}
```
- `keys()` :回傳一個所有 `keys` 的 `dict_keys`(類似 `list` )。
```python=
d = {'a': 1, 'b': 2}
print(d.keys()) # dict_keys(['a', 'b'])
```
- `values()` :回傳一個所有 `values` 的 `dict_values` (類似 `list` )。
```python=
d = {'a': 1, 'b': 2}
print(d.values()) # dict_values([1, 2])
```
- `get(key)` :則回傳 `key` 在 `dict` 裡面對應的 `value`。若 `key` 不在 `dict` 裡面,則會回傳 `None`。
```python=
d = {'a': 1, 'b': 2}
print(d.get('a')) # 1
print(d.get('c')) # None
```
- `items()` :回傳一個所有 `keys` 和 `values`(每組數據是 `tuple` 型態) 的 `dict_items`(類似 `list`)。
```python=
d = {'a': 1, 'b': 2}
print(d.items()) # dict_items([('a', 1), ('b', 2)])
```
- `pop(key)` :則刪除該組 `key` 和 `value` ,並回傳 `value` 。若 `key` 不在 `dict` 裡面,則會發生錯誤。
```python=
d = {'a': 1, 'b': 2}
print(d.pop('a')) # 1
print(d) # {'b': 2}
print(d.pop('c')) # KeyError: 'c'
```
- `popitem()` :刪除並返回最後加入(也就是 `dict` 的最後一項)的 `(key, value)` (`tuple` 型態)。若 `dict` 沒有元素,則會發生錯誤。
```python=
d = {'a': 1, 'b': 2}
print(d.popitem()) # ('b', 2)
print(d) # {'a': 1}
print(d.popitem()) # ('a', 1)
print(d) # {}
print(d.popitem()) # KeyError: 'popitem(): dictionary is empty'
```
### Function
- `len(dict)` :回傳 `dict` 的長度。
```python=
d = {'a': 1, 'b': 2}
print(len(d)) # 2
```
### Traversal
- 直接迭代,用 `key`、`dict[key] = value` 方式呈現。
```python=
d = {'a': 1, 'b': 2}
for key in d: # d 相當於 d.keys()
print('key:', key, 'value:', d[key])
'''
key: a value: 1
key: b value: 2
'''
```
- 用 `key`, `value` 迭代 `dict_items()` 。
```python=
d = {'a': 1, 'b': 2}
for key, value in d.items():
print('key:', key, 'value:', value)
'''
key: a value: 1
key: b value: 2
'''
```
- 用 `zip()` 把 `d.keys()` 和 `d.values()` 壓縮起來一起迭代。
```python=
d = {'a': 1, 'b': 2}
for key, value in zip(d.keys(), d.values()):
print('key:', key, 'value:', value)
'''
key: a value: 1
key: b value: 2
'''
```
## Discord Bot Set up
1. Register and log in [Discord](https://discord.com) (也有手機 App 版)
2. Enter in [Discord Developer](https://discord.com/developers/applications)
3. Click `New Application`
![](https://i.imgur.com/9nQU8oR.png =250x)
4. Create an `Application`
![](https://i.imgur.com/hUoo1LG.png =250x)
5. Click `Bot`
![](https://i.imgur.com/kv0S40Z.png =250x)
6. Click `Add Bot`
![](https://i.imgur.com/awZq8mN.png =250x)
7. Back to Discord
需要一個 `server` (伺服器、群組) 放 `bot`,
而這個必須擁有 `管理者` 的身分,
可以自己創一個 `server` 就一定會有管理者的身分,
別人的群組則大部分不會開管理者給成員。
8. Click `QAuth2`
![](https://i.imgur.com/MXn7eOs.png =250x)
9. Click `bot`
![](https://i.imgur.com/D89xqlv.png =250x)
10. Click these
![](https://i.imgur.com/hTEUeqC.png =250x)
11. Click `Copy`
![](https://i.imgur.com/JDU0g2W.png =250x)
12. 將連結貼至瀏覽器上
13. 選擇一個 server 並按下 `繼續`
![](https://i.imgur.com/Yqzw2mG.png =250x)
16. Back to Discord, and the bot is ready.
## 修飾器
如果要將某個函式放入該函式的參數中,
可以使用修飾器。
### Example
```python=
def modify_function(any_func):
print(type(any_func))
return lambda: type(any_func) #型態只能是function
@modify_function
def func():
pass
print(func()) # <class 'function'>
# <class 'function'>
```
## async 與 await --- 協程 (coroutine)
`async` 意指異步執行,
而 `await` 則是用來驅動 `async` 函式的必須指令。
在 Python 3.5 之前,協程函式只能用以下方法實作:
```python=
import asyncio
loop = asyncio.get_event_loop()
@asyncio.coroutine
def func():
return 10
print(loop.run_until_complete(func())) # 10
```
在協程函式中會放置 `return` 或是 `yield from`,
兩者都會回傳 `generator`,
要使用 `loop.run_until_complete()` 才能回傳真實結果。
Python 3.5 之後,
使用下列方法:
```python=
import asyncio
loop = asyncio.get_event_loop()
async def func():
return 10
print(loop.run_until_complete(func()))
"""
in async block
-> print(await func())
"""
```
如果在 `async` 區塊中使用協程時,可以使用 `await`。
## Start Coding
IDE(Integrated Development Environment) : repl.it
### Import Modules
```python=
import nextcord
from nextcord.ext import commands
```
### Declaration a bot
```python=
bot = commands.Bot(command_prefix = '$') # 宣告 bot
bot.remove_command('help') # 移除 built-in function
```
- `command_prefix` : 指令前面要加的輸入
- 因為 `bot` 也需要用到 `help`,所以把內建的 `help` 函數移除
## Event
### on_ready
```python=
@bot.event
async def on_ready():
await bot.change_presence(status = nextcord.Status.dnd,\
activity = nextcord.Activity(name = '$help',\
type = nextcord.ActivityType.listening))
print('>> Bot is online ')
```
- 當 `bot` 開始執行時會進入這個函數
- `status` 是 `bot` 的狀態
- `activity` 是 `bot` 的動作
#### Status
- `dnd (do_not_disturb)` : 勿擾
- `idle` : 閒置
- `offline` : 下線
- `online` : 上線
- `invisible` : 不可見
#### Activity
- `Streaming`
- `Activity`
- `Game`
```python=
import nextcord
s = nextcord.Streaming(name="...", url="twich_url")
# ... 實況中
"""
playing: 正在玩
listening: 正在聽
watching: 正在看
"""
a = nextcord.Activity(name="...", type=nextcord.ActivityType.something)
# 正在{somthing} ...
# 像是 正在玩 遊戲
# something 換成 playing , ... 換成遊戲
```
### on_member_join
```python=
@bot.event
async def on_member_join(member):
channel = bot.get_channel('Channel') # 自己的 channel 不是字串喔
await channel.send(f'{member} welcome!')
```
- 當有成員離開 `server` 時會進入這個函數
- channel 是自己頻道的編號(也就是網址 url 的最後一個 `/` 後面一整串)
### on_member_remove
```python=
@bot.event
async def on_member_remove(member):
channel = bot.get_channel('Channel') # 自己的 channel 不是字串喔
await channel.send(f'{member} leave!')
```
- 當有成員進入 `server` 時會進入這個函數
- `channel` 是自己頻道的編號(也就是網址 `url` 的最後一個 `/` 後面一整串)
### on_message
```python=
@bot.event
async def on_message(msg):
if msg.author != bot.user:
await msg.channel.send(msg.content)
# 你說什麼,就會回傳什麼
```
### on_message_delete
```python=
@bot.event
async def on_message_delete(msg):
await msg.channel.send(msg.content)
# 將你刪除的訊息回傳
```
### on_raw_reaction_add
```python=
@bot.event
async def on_raw_reaction_add(payload):
c = bot.get_channel(payload.channel_id)
await c.send(payload.emoji.name)
# 你在哪則訊息點了某個表情符號,它就會在訊息的頻道傳送那個表情符號
```
### on_raw_reaction_remove
```python=
@bot.event
async def on_raw_reaction_remove(payload):
c = bot.get_channel(payload.channel_id)
await c.send(payload.emoji.name)
# 你在哪則訊息取消了某個表情符號,它就會在訊息的頻道傳送那個表情符號
```
## Command
### Format
```python=
@bot.command()
async def func_name(ctx):
await ctx.send('欲回傳的訊息')
```
- `func_name` 是函數的名稱
- `ctx` 是指 `context` 上下文
- `欲回傳的訊息` 必須是字串
### Example
#### ping
- `latency` 中文是等待時間,相當於 `bot` 的延遲。
```python=
@bot.command()
async def ping(ctx):
await ctx.send(f'{round(bot.latency * 1000)} (ms)')
```
#### now
- 獲取現在時間 (UTC+8)。
```python=
import datetime
def gettime():
x = datetime.datetime.now()
err = datetime.timedelta(hours=8)
x += err
y = x.year
m = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][x.month - 1]
d = x.day
h = x.hour
mi = x.minute
s = x.second
w = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][(x.weekday() + 1) % 7]
res = '{} {} {:02d} {:02d}:{:02d}:{:02d} {}'.format(w, m, d, h, mi, s, y)
return res
@bot.command()
async def now(ctx):
await ctx.send(gettime())
```
#### calc
- 計算一條算式。
```python=
from math import *
def operation(s):
s = s.replace(' ', '')
s = s.replace('^', '**')
s = s.replace('log', 'log10')
s = s.replace('ln', 'log')
i, t = len(s) - 1, 0
while i >= 0: # 處理 "factorial 階乘"
if s[i] == '!':
if s[i - 1].isdigit():
t, i = i, i - 1
while s[i].isdigit():
i -= 1
tmp = s[i + 1: t]
s = s[:i + 1] + 'factorial(' + tmp + ')' + s[t + 1:]
else:
t, right, i = i, 1, i - 2
while right:
if s[i] == ')':
right += 1
if s[i] == '(':
right -= 1
i -= 1
tmp = s[i + 1:t]
s = s[:i + 1] + 'factorital(' + tmp + ')' + s[t + 1:]
i -= 1
# print(s)
try:
res = round(eval(s), 3)
return res
except:
res = '(type error or too difficult)'
return res
@bot.command()
async def calc(ctx, *, s):
ans = func.operation(s)
await ctx.send(f'result of ({s}) is {ans}')
```
#### guess
- 隨機猜 `[1, 5]` 的整數,猜中了會回傳 `True`;反之,`False`。
```python=
def Guess(n):
tmp = randint(1, 5)
a = ['false', 'true']
s = f'right ans is {tmp}, your ans is {a[n == tmp]}'
return s
@bot.command()
async def guess(ctx, n : int):
await ctx.send(Guess(n))
```
#### help
- 可以告訴你這個 `bot` 有哪些功能。
- 以表單的形式呈現。
```python=
bot.remove_command('help') # 要先移除原本的 help 指令
@bot.command()
async def help(ctx):
embed = nextcord.Embed(color = 0x86ff00)
embed.set_author(name = jdata['FORM'])
embed.set_thumbnail(url = jdata['STICKER'])
embed.add_field(name = '$help',\
value = 'To get help',\
inline = False)
embed.add_field(name = '$calc',\
value = 'To calculate your formula',\
inline = False)
embed.add_field(name = '$now',
value = 'To get the current time',\
inline = False)
embed.add_field(name = '$ping',\
value = 'To get the ping delay',\
inline = False)
embed.add_field(name = '$guess',\
value = 'To guess a number (range : 1 ~ 5)',\
inline = False)
embed.add_field(name = '$hunt',\
value = 'To hunt your zerojudge data',\
inline = False)
await ctx.send(embed = embed)
```
## Keep Alive
> 完全參考 [[Proladon] Code a discord bot - 如何讓Discord Bot 24小時在線](https://reurl.cc/MZ45yW)
1. 下載 [雲端相關文件](https://reurl.cc/lLl3Xl)。
2. 進入 repl.it 並註冊帳號。
3. 點擊左邊 `+ New repl`,選擇 `PYTHON` 語言,打上這個專案的名字,按下 `Create repl`。
![](https://i.imgur.com/CSpv9oR.png)
4. 將原本的程式碼和雲端文件的 `keep_alive.py` 貼至 `main.py` 裡面。
5. 在 `main.py` 最上面加入 `import keep_alive`。
6. 在 `main.py` 加入 `keep_alive.keep_alive()`,位置如下:
```python=
if __name__ == "__main__":
keep_alive.keep_alive() # 加在這裡
bot.run(jdata['TOKEN'])
```
7. 儲存後按下 `run` (第一次需要下載套件,要等會兒)
8. 進入監控 `bot` 的 [網站](https://uptimerobot.com/) ,註冊並登入帳號。
9. 點擊左上角 `+Add New Monitor`,`Monitor Type` 選 `HTTP(s)`,`URL (or IP)` 貼 `repl` 右上方的網址 (如下圖 1.),`Monitoring Interval` 是 `監控間隔`,用最短的 `5 minutes` 就好,`Select "Alert Contacts To Notify"` 是 `bot` 有離線或重新上線是否需要 `email` 通知,視個人決定要不要勾選。最後會變成如下圖 2.,按下 `Create Monitor`。 (第一次需要一點時間抓取資料,要等會兒)
![](https://i.imgur.com/abq1l4y.png) 圖 1.
![](https://i.imgur.com/3KI778s.png) 圖 2.
10. 完成。
## 其它檔案
如果你的機器人在其它檔案,那你要如何載入呢?
有一個方法適用,
創建一個資料夾叫做 `core`
之後,在其中創建一個 `classes.py` 的檔案,
檔案架構如下:
```
| main.py
├───cmds
| cmd1.py
| cmd2.py
└───core
classes.py
```
`classes.py` 的內容如下 :
```python=
import nextcord
from nextcord.ext import commands
class Cog_(commands.Cog):
def __init__(self, bot):
self.bot = bot
def setup(bot):
bot.add_cog(Cog_(bot))
```
其他檔案在 `cmds` 裡的檔案,
像 `cmd1.py` 和 `cmd2.py`,
則需要在開頭加入 `from core.classes import Cog_`
以及在結尾加入
```python=
def setup(bot):
bot.add_cog(Your_Class(bot))
```
檔案內容寫法有些不同。
拿 `ping` 來舉例,
要將程式碼改成如下才有辦法運作
會變成:
```python=
import nextcord
from nextcord.ext import commands
from core.classes import Cog_
class Your_Class(Cog_):
@commands.command()
async def ping(self, ctx):
await ctx.send(f'{round(self.bot.latency*1000)} (ms)')
def setup(bot):
bot.add_cog(Your_Class(bot))
```
`Your_Class` 的名字能自己取,基本上你要自己取就自己取
現在,讓我問問你們,有哪裡不一樣了?
答案揭曉
- 在修飾器中的 `bot` 變成 `discord.ext` 裡的 `commands` 了
- `bot` 都改成 `self.bot` 了
- 函式的參數多了一個 `self`
`self` 是自身的意思,
代表著這整個 `class`,
`self.bot` 就等於這整個 `class` 的 `bot` 這個屬性
所有 `bot.command()` 都要換成 `commands.command()`
而 `bot.event` 則是換成 `commands.Cog.listener()`
最後一個步驟則是在 `main.py` 裡加入下列程式碼,
以在開啟時能載入。
```python=
import os
for filen in os.listdir('./cmds'):
if filen.endswith('.py'):
bot.load_extension(f'cmds.{filen[:-3]}')
```
## Music (進階)
音樂最基本功能當然是播放歌曲啦 XD,
所以要來教你如何編寫 play 指令。
### 安裝套件
你可以安裝 `Black_Lotus` 寫的套件,
請使用指令 : `pip install NPytdl==4.1.0b2` [Source](https://reurl.cc/Gd4R6p)
### p, play
```python=
import nextcord
from nextcord.ext import commands
from NPytdl import Pytdl
ydl = Pytdl()
OPTIONS =
{'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5',\
'options': '-vn'}
@bot.command(aliases=["p"])
async def play(ctx: commands.Context, *, url: str):
if url.find("https://") != -1:
if url.find("list=") != -1:
List = await ydl.info(url)
await List.create()
songs = List.videos
else:
song = await ydl.info(url)
await song.create()
songs = [song]
else:
data = await (ydl.resultList(url))[0]
song = await ydl.info(f"https://youtu.be/{data['id']}")
await song.create()
songs = [song]
client = ctx.voice_client
if client is None:
for voice_client in self.bot.voice_clients:
if voice_client.channel in ctx.guild.channels:
client = voice_client
if client is None:
if ctx.author.voice is not None:
client = await ctx.author.voice.channel.connect()
if client is None:
await ctx.send("你要加入頻道!!!")
return
source = discord.FFmpegOpusAudio(songs[0].voice_url, **OPTIONS)
client.play(source)
```
此指令在以下情況可能會有錯誤產生:
- 機器人已在播音樂
- 爬蟲發生錯誤
- 機器人未加入頻道
- 電腦沒安裝`FFmpeg`
- Python 未安裝 `PyNaCl`