## 起手式
先印出個`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
```