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