## 起手式 先印出個`Hello World`試水溫,使用單引號和雙引號效果是相同的, 但像是要印出`I'm ok`這樣的結果就需要用雙引號包單引號 ```py print("Hello World") # Hello World print('Hello World') # Hello World print("I'm ok") # I'm ok ``` 將字串相加在一起可以使用以下方式: ```py print("Hello " + "World") # Hello World ``` 有更好的方式可以處理字串格式,使用`format()`方法表示一個蘿蔔一個坑概念 有幾個`{}`在`format()`中,代表有幾個變數。 ```py name = "Jeter" age = 18 print("My name is {}, I'm {} years old".format(name, age)) # My name is Jeter, I'm 18 years old ``` 在`Python3.6`以後出現了`f-string`更簡單明瞭。 ```py name = "Jeter" age = 18 print(f"My name is {name}, I'm {age} years old") # My name is Jeter, I'm 18 years old ``` 事實上`f-string`並不只限於此功能,還可以做到對齊功能,在:後始加上^15表示寬度有15個空白字,可以省去計算左右空白的問題,用聖誕樹來做個範例。 ```py 聖誕樹 for i in range(5): print(f"{'*' * (2 * i + 1):^15}") print(f"{'*':^15}") print(f"{'*':^15}") 根據上述結果會打印出: * *** ***** ******* ********* * * ``` ## 變數與基本運算 在程式語言的世界中, `=`這個符號是指定的意思。 例如`a = b`代表的就是把`b`指定給`a`。 根據以下例子意思是,將10指定給num這個變數。 ```py num = 10 num = num + 1 print(a) # 11 ``` `num = num + 1`這個寫法有簡寫,例如: ```py num += 1 ``` 相對的其他運算符也有簡寫,例如: ```py num -= 1 num *= 2 num /= 2 ``` `python`和其他程式語言一樣有`+ - * /`等數字運算符, 以下利用例子來講解,其中值得注意的是運算符`/ and //`, 使用`/`得出結果會是`float`浮點數,使用`//`會是正整數 ```py num = 1 + 1 print(num) # 2 num = 1 - 1 print(num) # 0 num = 2 * 3 print(num) # 6 num = 4 / 2 print(num) # 2.0 num = 4 // 2 print(num) # 2 num = 5 % 3 # 取餘數 print(num) # 2 num = 2 ** 3 # 次方 print(num) # 8 ``` ## 資料型態 數字型態:`int、float、bool` 字串型態:`str` 容器型態:`list、tuple、set、dict` 1.整數 ```py number = 10 print(number) # 10 print(type(number)) # <class 'int'> ``` 2.浮點數 ```py number = 10.7 print(number) # 10.7 print(type(number)) # <class 'float'> ``` 3.布林值 布林值設定為兩種值:`True`和`False`, 在`Python`中`True`和`False`又分別代表1和0 ```py x = True print(x) # True print(type(x)) # <class 'bool'> ``` ```py x = True + 1 y = False + 1 print(x) # 2 print(y) # 1 ``` 4.字串 使用`單引號 ' '`or`雙引號 " "`都可以 ```py world = "hello world" print(world) # hello world print(type(world)) # <class 'str'> ``` 字串可以做相加 ```py print("hello" + " world") # hello world ``` 字串可以重複打印 ```py world = "hello world" print(world * 5) # hello worldhello worldhello worldhello worldhello world ``` 字串可以計算長度 ```py world = "hello world" print(len(world)) # 11 ``` 字串可以取出內容 使用`[]`取索引值,像是`JavaScript`取`index` ```py 這個方式是從前面開始取值 world = "hello world" print(world[1]) # e 如果要取最後一位數可以使用 -1 print(world[-1]) # d ``` 在`Python`中有一個切片的方式稱為`slice` 例如以下範例中,從第一位開始取到第五位,但不包含第五位 ```py world = "hello world" print(world[1:5]) # ello ``` ## if-else判斷式 在Python中判斷式可使用`if、elif、else`來實現各種流程控制 ```py weather = "sun" if weather == "sun": print("出門go!go!") elif weather == "rain": print("在家不出門") else: print("隨便") ``` ## 迴圈 在`Python`中有個叫做`串列(list)`的結構,用法類似於其他語言的`Array list`使用中括號將資料包起來,搭配迴圈可印出串列中內容 for迴圈 ```py list_numbers = [1, 2, 3, 4, 5] for i in list_numbers: print(i) # 1 2 3 4 5 ``` `串列(list)`不只可以取數字,還可以取文字or物件 ```py list_str = ["a", "b", "c", "d"] for i in range(len(list_str)): print(list_str[i]) # a b c d 或是以下方式 list_str = ["a", "b", "c", "d"] for i in list_str: print(i) # a b c d ``` 在上述範例中有看到使用`range()`方法,實際內容為 `range(start, stop, step)` 表示說起始值, 停止值, 步長 在這當中`stop`是唯一必需的值,如果只有輸入一個參數則表示`stop` ```py for i in range(0, 10, 2): print(i) # 0 2 4 6 8 ``` `break`和`continue` 迴圈結束分為規則和不規則, `break`:中斷迴圈執行並跳脫迴圈,繼續執行迴圈外的程式碼。 `continue`:迴圈不會結束,只是跳過迴圈內`continue`後面剩下的程式碼繼續執行下一次的迴圈。 ```py for i in range(10): if i == 5: break print(i) # 0 1 2 3 4 ``` ```py for x in range(10): if x == 5: continue print(x) # 0 1 2 3 4 6 7 8 9 ``` `while`迴圈 基本上`for`迴圈能做到的事也能用`while`迴圈做到,唯一有差別的 在於知道要執行次數時使用`for`迴圈,不知道執行次數時用`while`迴圈,例如猜謎遊戲就適合使用`while`迴圈。 ```py total = 0 while total < 10: print(total) total += 1 ``` 猜謎遊戲 ```py import random secret = random.randint(1, 100) min_num = 1 max_num = 100 while True: guess = int(input(f"{min_num} ~ {max_num} 之間: ")) if guess < min_num or guess > max_num: print("答案不在範圍內,請重新輸入:") continue if guess == secret: print(f"恭喜!答對了,答案是{secret}") break elif guess < secret: min_num = guess elif guess > secret: max_num = guess ``` ## 串列 串列可以用來存取有順序性的資料,想放`數字`、`字串`、`布林值`、`None`都可以。 ```py names = ["Jason", "Jeter", "Jay", "John"] print(names) # ['Jason', 'Jeter', 'Jay', 'John'] # len()可以計算list的長度,字串、字典、集合、tuple等都可以使用 print(len(names)) # 4 # 索引值可用 [ ] 來取得 print(names[1]) # Jeter # 要取得最後一位元素方式,在上方有提到使用 -1 來取得 print(names[-1]) # John # 索引值不只可以取值用,也可把元素替換 names[1] = "Jack" print(names[1]) # Jack # 如果取到串列中沒有的元素會報錯 print(names[5]) # IndexError: list index out of range # 要取一個範圍內的元素可使用切片的方式 names = ["Jason", "Jeter", "Jay", "John", "Josh", "Judy"] print(names[0:3]) # ['Jason', 'Jeter', 'Jay'] print(names[:2]) # ['Jason', 'Jeter'] print(names[2:]) # ['Jay', 'John', 'Josh', 'Judy'] ``` 增加串列(list)裡的元素可使用`append()`方法。 ```py names = ["Jason", "Jeter", "Jay", "John", "Josh", "Judy"] names.append("JoJo") print(names) # ['Jason', 'Jeter', 'Jay', 'John', 'Josh', 'Judy', 'JoJo'] ``` 增加至指定位置可使用`insert()`方法。 ```py names = ["Jason", "Jeter", "Jay", "John", "Josh", "Judy"] names.insert(2, "Jenny") print(names) # ['Jason', 'Jeter', 'Jenny', 'Jay', 'John', 'Josh', 'Judy'] 使用insert()方法也是可以將兩串列合併在一起,但會發生問題是以串列的形式 加入,例如以下範例: names = ["Jason", "Jeter", "Jay", "John", "Josh", "Judy"] name = ["Andy", "Ann", "Annie", "abc"] names.insert(0, name) print(names) # [['Andy', 'Ann', 'Annie', 'abc'], 'Jason', 'Jeter', 'Jay', 'John', 'Josh', 'Judy'] 要改善這樣的方法就需要使用到extend()方法 ``` 一次性加入多個元素or將某串列內容加入到另一串列內時可使用`extend()`方法,使用`extend`方法時會將原先的內容改變。 ```py names = ["Jason", "Jeter", "Jay", "John", "Josh", "Judy"] name = ["Andy", "Ann", "Annie", "abc"] names.extend(name) print(names) # ['Jason', 'Jeter', 'Jay', 'John', 'Josh', 'Judy', 'Andy', 'Ann', 'Annie', 'abc'] ``` 移除串列中元素可用`remove()`方法。 ```py names = ["Jason", "Jeter", "Jay", "John", "Josh", "Judy"] names.remove("Judy") print(names) # ['Jason', 'Jeter', 'Jay', 'John', 'Josh'] ``` 移除串列最後一個元素可用`pop()`方法,`pop()`方法會回傳移除的元素。 ```py names = ["Jason", "Jeter", "Jay", "John", "Josh", "Judy"] pop_name = names.pop() print(names) # ['Jason', 'Jeter', 'Jay', 'John', 'Josh'] print(pop_name) # 'Judy' ``` `sort()`方法會按照遞增方式排列,這方法會改變原本的串列。 ```py number = [1, 4, 6, 2, 3, 9, 69, 10, 8] number.sort() print(number) # [1, 2, 3, 4, 6, 8, 9, 10, 69] ``` `sorted()`方法,不改變原本串列就使用sorted 這方式可以保存資料的完整性且不輕易被修改,同時又整理過。 ```py number = [1, 4, 6, 2, 3, 9, 69, 10, 8] num = sorted(number) print(num) # [1, 2, 3, 4, 6, 8, 9, 10, 69] ``` `reverse()`方法,反轉串列。 ```py number = [1, 2, 3, 4, 5] number.reverse() print(number) # [5, 4, 3, 2, 1] ``` `max()` `min()`最大值和最小值。 ```py number = [1, 2, 3, 4, 5] num = max(number) print(num) # 5 num = min(number) print(num) # 1 ``` `sum()`方法,加總。 ```py number = [1, 2, 3, 4, 5] num = sum(number) print(num) # 15 ``` `index()`方法,查詢索引值。 ```py names = ["Jason", "Jeter", "Jay", "John", "Josh", "Judy"] name = names.index("Jay") print(name) # 2 如果查詢不存在串列中的元素時會出現錯誤: name = names.index("abc") print(name) # ValueError: 'abc' is not in list ``` `in()`方法,查詢是否存在清單中,此方式會回傳True or False。 ```py names = ["Jason", "Jeter", "Jay", "John", "Josh", "Judy"] print("Jeter" in names) # True print("abc" in names) # False ``` 串列推導式 適合用在簡單的例子中,如果有兩層迴圈或是`if-else`判斷則不適合,建議還是拆開使用,完全看個人習慣。 以下兩段程式碼結果是相同的,只是寫法不同。 ```py # 程式碼一: numbers = [num for num in range(5)] print(numbers) # [0, 1, 2, 3, 4] # 程式碼二: numbers = [] for num in range(5): numbers.append(num) print(numbers) # 印出 [0, 1, 2, 3, 4] ``` 串列開箱運算子 * ```py number = [1, 2, 3, 4, 5] name = ["Jeter", "Jason", "Jack"] value = [*number, *name] print(value) # [1, 2, 3, 4, 5, 'Jeter', 'Jason', 'Jack'] ``` ## 字典 字典的最外層使用大括號`{ }`包起來,裡面則是由「鍵(Key)」以及「值(Value)」所組成。建立字典不需要使用任何函數,直接用大括號就可以開始寫。 ```py # name、age就是key,Jeter、18是value data = {"name": "Jeter", "age": 18} ``` #### 取用字典 ```py user = {"name": "Jeter", "age": 20, "power": 99} print(user["name"]) # Jeter print(user["power"]) # 99 ``` #### 更新、增加 ```py user = {"name": "Jeter", "age": 20, "power": 99} user["name"] = "Jason" print(user["name"]) # Jason # 這方式看起來是把height改成175,但原本字典中並沒有height,所以會判斷成要新增 user["height"] = 175 print(user) # {'name': 'Jason', 'age': 20, 'power': 99, 'height': 175} # 更新一個或多個值,update實際上更像是合併的概念 user = {"name": "Jeter", "age": 20, "power": 99} data = {"name": "Jason", "age": 50, "power": 33} user.update(data) print(user) # {'name': 'Jason', 'age': 50, 'power': 33} ``` #### 常見操作 ```py # 計算個數 user = {"name": "Jeter", "age": 20} print(len(user)) # 2 # 清除內容 user = {"name": "Jeter", "age": 20} print(user.clear()) # {} # 取出,pop()會取出最後一個 user = {"name": "Jeter", "age": 20} result = user.pop("age") print(user) # {'name': 'Jeter'} ``` 字典開箱運算子 ** ```py city = {"name": "台北", "population": 2600000} location = {"lat": 25.04, "lng": 121.51} data = {**city, **location} print(data) # {'name': '台北', 'population': 2600000, 'lat': 25.04, 'lng': 121.51} ``` 複製,`.copy()`方法只能複製到第一層的內容,如果字典中有第二、三...層,則無法複製。 ```py name = {"name": "Jeter", "age": 20} copy_name = name.copy() copy_name["name"] = "Jason" print(copy_name) # {'name': 'Jason', 'age': 20} print(name) # {'name': 'Jeter', 'age': 20} ``` 印出所有的keys and values,`.keys()` `.values()` ```py name = {"name": "Jeter", "age": 20} print(name.keys()) # dict_keys(["name", "age"]) print(name.values()) # dict_values(["Jeter", 20]) ``` 或是使用`for`迴圈也可以 ```py name = {"name": "Jeter", "age": 20} for i in name.keys(): print(i) # name age for j in name.values(): print(j) # Jeter 20 ``` 或是使用`.items()`方法 ```py name = {"name": "Jeter", "age": 20} print(name.items()) # dict_items([('name', 'Jeter'), ('age', 20)]) ``` 字典推導式 ```py fruits = ["蘋果", "香蕉", "鳳梨", "芭樂"] prices = [20, 10, 15, 30] fruit_price = {fruit: price for fruit, price in zip(fruits, prices)} print(fruit_price) # {'蘋果': 20, '香蕉': 10, '鳳梨': 15, '芭樂': 30} ``` ## 元組與集合 `tuple()`是不可變的,建立好後無法`新增`、`移除`、`修改`等,內容想放什麼就放什麼,沒有特別規定。 建立`tuple()` ```py number = (123.45, 66) print(type(number)) # <class 'tuple'> ``` 或是可以用`tuple()`把陣列或是字串等可迭代物件轉換成`tuple`。 ```py number = [1, 2, 3] print(tuple(number)) # (1, 2, 3) name = "Jeter" print(tuple(name)) # ('J', 'e', 't', 'e', 'r') ``` 如果`tuple`只有一個參數的話,結尾必須加上`,`,否則會被當作是其他的資料型態。 ```py not_tuple = ("hello") print(type(not_tuple)) # <class 'str'> not_tuple = ("hello",) print(type(not_tuple)) # <class 'tuple'> ``` 雖然說`tuple`是不可變的,但`tuple`中有可變的資料型態還是可以操作的。 ```py name = ("jeter", 100, ["Jason", "Jack", "Jay"]) name[-1][1] = "Judy" print(name) # ('jeter', 100, ['Jason', 'Judy', 'Jay']) ``` - 資料不需要變動情況下,ex. 經緯度,使用`tuple`。 - 資料型態相同時使用`list`,ex. `str`、`int`,資料型態不同時使用`tuple`。 `set{}`,特性為`不重複`、`無順序`。 ```py number = {1, 3, 3, 3, 3, 3, 2, 4} print(number) # {1, 2, 3, 4} ``` 可迭代的東西都可放進`set`,如果一個`list`有重複的內容想移除,可以透過`set`。 ```py name = ["Jeter", "Jason", "Jack", "Jeter"] all_name = list(set(name)) print(all_name) # ['Jeter', 'Jack', 'Jason'] ``` 但其實也不是所有東西都可丟到`set`裡面 ```py num = {[1, 2, 3], 4, 5} print(num) Traceback (most recent call last): line 1, in <module> num = {[1, 2, 3], 4, 5} ^^^^^^^^^^^^^^^^^ TypeError: unhashable type: 'list' ``` 新增元素 `add()`,如果新增重複的元素一樣會被過濾掉。 ```py num = {1, 2, 3, 4} num.add(66) print(num) # {1, 2, 3, 4, 66} ``` 刪除元素 `remove()` ```py num = {1, 2, 3, 4} num.remove(1) print(num) # {2, 3, 4} ``` 清空元素 `clear()` ```py num = {1, 2, 3, 4} num.clear() print(num) # set() ``` 複製集合 `copy()` ```py num = {1, 2, 3, 4} s1 = num.copy() s2 = set(num) print(s2) # {1, 2, 3, 4} ``` 交集,使用`&`運算子或是`.intersection()`方法。 ```py s1 = {9, 5, 2, 7} s2 = {1, 4, 5, 0} num = s1 & s2 print(num) # {5} num = s1.intersection(s2) print(num) # {5} ``` 聯集,使用`|`運算子或是`.union()`方法。 ```py s1 = {9, 5, 2, 7} s2 = {1, 4, 5, 0} num = s1 | s2 print(num) # {0, 1, 2, 4, 5, 7, 9} num = s1.union(s2) print(num) # {0, 1, 2, 4, 5, 7, 9} ``` 差集,使用`-`運算子或是`.difference()`方法,交集和聯集沒有順序問題,但是差集指比較第一個元素裡有,而第二個元素裡沒有的值。 ```py s1 = {9, 5, 2, 7} s2 = {1, 4, 5, 0} num = s1 - s2 print(num) # {9, 2, 7} num = s1.difference(s2) print(num) # {9, 2, 7} ``` 集合之間有無交集不一定得用上面方式,可以用`isdisjoint()`方法。 ```py s1 = {9, 5, 2, 7} s2 = {1, 4, 5, 0} num = s1.isdisjoint(s2) print(num) # False ``` ## 函數 輸入值和輸出值的對應關係。 #### 位置引數 ```py def calc_bmi(height, weight): return height, weight print(calc_bmi(170, 60)) # (170, 60) ``` #### 關鍵字引數 ```py def bmi(height, weight): return height, weight print(bmi(height=170, weight=60)) # (170, 60) print(bmi(weight=60, height=170)) # (170, 60) ``` 位置引數需要按照順序擺放,關鍵字引數則不需要按照順序,但如果兩者混合使用時,位置引數需要在關鍵字引數之前。 ```py def bmi(height, weight): return height, weight print(bmi(170, weight=60)) # (170, 60) print(bmi(height=170, 60)) # SyntaxError: positional argument follows keyword argument ``` #### / 和 * `/` => 在這之前只能使用位置引數 ```py def num(a, b, c, /, d, e): return a, b, c, d, e print(num(1, 2, 3, 4, 5)) # (1, 2, 3, 4, 5) print(num(1, 2, 3, d=4, e=5)) # (1, 2, 3, 4, 5) print(num(1, 2, c=3, d=4, e=5)) # TypeError: num() got some positional-only arguments passed as keyword arguments: 'c' ``` `*` => 在這之後只能使用關鍵字引數 ```py def num(a, b, c, *, d, e): return a, b, c, d, e print(num(1, 2, 3, d=4, e=5)) # (1, 2, 3, 4, 5) print(num(1, 2, c=3, d=4, e=5)) # (1, 2, 3, 4, 5) print(num(1, 2, 3, 4, 5)) # TypeError: num() takes 3 positional arguments but 5 were given ``` 參數預設值 有預設值需要放在沒有預設值的參數之後,否則會出現`SyntaxError: parameter without a default follows parameter with a default`的錯誤訊息 ```py def hello(name, age, message="hello"): return f"{message}, 我是{name}, 今年{age}" print(hello("Jeter", 20)) ``` 引數開箱 ```py def hi(a, b, c): print(a, b, c) heros = ["Jeter", "Jason", "Jay"] hi(*heros) # Jeter Jason Jay heroes = {"a": "Jeter", "b": "Jason", "c": "Jay"} hi(**heroes) # Jeter Jason Jay ``` LEGB 規則 `python`的作用域分成四種,分別是 - 區域(Local, L),在函數內有定義的話會優先使用。 - 封閉(Enclosing, E),函數裡面還有函數的話會先看自己這層有無定義,沒有的話會往外層找。 - 全域(Global, G),每層函數都找不到或是函數只有一層時,會往全域找。 - 內建(Built-in, B),全域都找不到沒有定義時才會找內建的函數或變數。 ## 物件導向 可想像成`物件`=`屬性` + `方法`。 ```py # 類別 class Hero: pass # 物件 hero = Hero() ``` 透過`__class__`可知道是哪個類別所產生的,透過`__name__`可以知道是哪個類別的名字。 ```py class Cat: pass hello = Cat() world = Cat() >>> hello <__main__.Cat object at 0x1042c7d10> >>> world <__main__.Cat object at 0x1043d0fe0> >>> isinstance(hello, Cat) True >>> isinstance(world, Cat) True >>> isinstance(world, str) False >>> hello.__class__ <class '__main__.Cat'> >>> hello.__class__.__name__ 'Cat' ``` ### 初始化`__init__` ```py class Cat: def __init__(self, name, color, age): self.name = name self.color = color self.age = age hello = Cat("Jeter", "orange", 18) world = Cat("Jason", "black", 20) print(hello.name) # Jeter print(world.age) # 20 ``` `__dict__` => 檢視這顆物件身上的屬性 ```py class Cat: def __init__(self, name, color, age): self.name = name self.color = color self.age = age hello = Cat("Jeter", "orange", 18) world = Cat("Jason", "black", 20) print(hello.__dict__) # {'name': 'Jeter', 'color': 'orange', 'age': 18} ``` ### `getattr()` 和 `setattr()` 一般來說使用`.`就可以了,比較直覺。 `getattr()` => 取得屬性 ```py class Cat: def __init__(self, name, color, age): self.name = name self.color = color self.age = age hello = Cat("Jeter", "orange", 18) world = Cat("Jason", "black", 20) print(getattr(hello, "name")) # Jeter ``` `setattr()` => 設定屬性 ```py class Cat: def __init__(self, name, color, age): self.name = name self.color = color self.age = age hello = Cat("Jeter", "orange", 18) world = Cat("Jason", "black", 20) setattr(hello, "name", "hello world.") print(getattr(hello, "name")) # hello world. ``` ### 實體方法 所有實體方法開頭都必須得加上`self`。 ```py class Cat: def __init__(self, name, color, age): self.name = name self.color = color self.age = age def speak(self, sound="你好"): print(f"我是{self.name},{sound}") hello = Cat("Jeter", "orange", 18) world = Cat("Jason", "black", 20) hello.speak() # 我是Jeter,你好 hello.speak("我是誰?") # 我是Jeter,我是誰? ``` ### 類別屬性 ```py class Cat: count = 0 actions = [] print(Cat.count) # 0 print(Cat.actions) # [] ``` ### 屬性裝飾器 ```py class Hero: def __init__(self, title, name, age): self.title = title self.name = name self._age = age def get_age(self): return self._age def set_age(self, age): if age <= 0 or age > 120: raise ValueError("年齡設定錯誤") self._age = age hero = Hero("金牌", "Jeter", 20) # 正常操作 print(hero.get_age()) # 20 hero.set_age(30) print(hero.get_age()) # 30 # 不正常操作 hero.set_age(999) print(hero.get_age()) # ValueError: 年齡設定錯誤 ``` 簡化`get_age` and `set_age`,不需要加上`()`就可使用。 ```py class Hero: def __init__(self, title, name, age): self.title = title self.name = name self._age = age @property # 這邊會得到 getter def age(self): return self._age @age.setter # 這邊會得到 setter def age(self, age): if age <= 0 or age > 120: raise ValueError("年齡設定錯誤") self._age = age hero = Hero("金牌", "Jeter", 20) # 正常操作 print(hero.age) # 20 hero.age = 30 print(hero.age) # 30 # 不正常操作 hero.age = 999 print(hero.age) # ValueError: 年齡設定錯誤 ``` ### 私有屬性 `python`中並沒有所謂的私有屬性,都還是可以取得的。 以下方式把`age`加上兩個`_`,不管是單個or雙底線都還是可以取得,只是雙底線取值會發現錯誤,這時可以利用`__dict__`來檢視這個物件的屬性是什麼,以例子來講可以看到`__age`變成了`_Hero__age`,所以就可以利用`_Hero__age`取值。 ```py class Hero: def __init__(self, name, age): self.name = name self.__age = age hero = Hero("Jeter", 20) print(hero.__age) # AttributeError: 'Hero' object has no attribute '__age' print(hero.__dict__) # {'title': '勇者', 'name': '欣梅爾', '_Hero__age': 18} print(himmel._Hero__age) # 20 ``` `__slots__` => 可以限制實體屬性,只有設定在裡面的內容才能使用,像是可以使用`tuple`來設定,就像是設定白名單那樣。 ```py class Hero: __slots__ = ["name", "age"] hero = Hero() hero.age = 20 print(hero.age) # 20 hero.number = 99 print(hero.number) # AttributeError: 'Cat' object has no attribute 'number' ``` ### 類別方法 透過`@classmethod`裝飾器定義。 下面例子來看,`all()`方法只是一般的方法沒有加上裝飾器,執行`Hero.list()`時會將類別`Hero`當作參數傳入,所以使用`__name__`就會取得`Duck`這個類別名稱。 - 可以直接用類別名稱呼叫,不需要建立實體。 - 只能存取類別層級的資料。 ```py class Hero: def all(): print("我是英雄!") @classmethod def list(cls): print(f"{cls.__name__}早上好!") Hero.list() # Hero早上好! ``` 例子: 可以直接操作`User.from_database(user_id=6)`。 ```py class User: def __init__(self, username, email, password): self.username = username self.email = email self.password = password @classmethod def from_json(cls, json_data): # 從 JSON 資料建立使用者 return cls( json_data['username'], json_data['email'], json_data['password'] ) @classmethod def from_database(cls, user_id): # 從資料庫讀取使用者資料建立實體 user_data = database.get_user(user_id) return cls( user_data['username'], user_data['email'], user_data['password'] ) user = User.from_database(user_id=6) # 直接用類別呼叫 ``` 如果是實體方法,就必須得先建立實體才能呼叫 ```py # 方法一 user = User("Jeter", "jeter@gmail.com", 12345) # 建立實體 # 方法二 user = User.from_database(user_id=6) # 再呼叫方法 ``` ## 繼承 用來讓共同的功能寫在上層類別中,就不需要在每一層都寫一樣的邏輯。 ```py class Primate: def grab(self, something=None): if something: return f"抓{something}" return "抓東西" class Human(Primate): pass class Monkey(Primate): pass monkey = Monkey() human = Human() print(monkey.grab("香蕉")) # 抓香蕉 print(human.grab("筷子")) # 抓筷子 ``` 使用`__mro__`可以查看所有類別繼承的相對關係,假如今天要在某個物件裡找屬性或方法時,會先看這物件自己有沒有,沒有的話會往它的所屬類別找,再沒有的話會往這個所屬類別的上層類別找,直到找不到為止。 ex. 從`Cat`類別出發找`add()`方法,發現 ```py class Animal: def add(self, a, b): return a + b class Human(Animal): pass class Cat(Mammal): pass print(Cat.__mro__) # (<class '__main__.Cat'>, <class '__main__.Human'>, <class '__main__.Animal'>, <class 'object'>) print(Human.__mro__) # (<class '__main__.Human'>, <class '__main__.Animal'>, <class 'object'>) print(Animal.__mro__) # (<class '__main__.Animal'>, <class 'object'>) cat = Cat() result = cat.add(1, 2) print(result) # 3 ```