# Python 冷知識 - 註解、函數、布林值

> 匯整一下近期整理的 Python 冷知識，就是那些知道了也不一定會變高手的趣味事實（至少我覺得...

Published: 2024-10-24
URL: https://kaochenlong.com/python-fun-facts

---

匯整一下近期整理的 Python 冷知識，就是那些知道了也不一定會變高手的趣味事實（至少我覺得有趣）。

&lt;!-- more --&gt;

## 關於多行註解

有寫過 Python 的朋友應該知道，在 Python 是使用 `#` 符號來當做程式碼「註解（Comment）」寫，但你可能不知道 Python 並沒有「多行註解」的設計喔！
你可能會好奇這樣的寫法：

```python
print(&quot;Hello&quot;)

&quot;&quot;&quot;
這是註解
這也是註解
這行還是註解
&quot;&quot;&quot;

print(&quot;World&quot;)
```

你看，用 3 個引號這個不就是多行註解了嗎？

如果你認為這是註解，那你可能誤解「註解」的設計了。

在 Python 這種 3 個引號的寫法並不是註解，它只是可以換行的「多行字串」而已。Python 本身只有單行註解，並沒有多行註解的設計。

的確，你可以拿多行字串來充當註解的效果，但不表示它就是註解，說到底它就只是個字串而已。通常使用多行文字充當註解的時候都不會指定變數給它，所以在 Python 直譯器的眼裡，它可能不會被編譯到 Bytecode 裡，以剛才的範例，編譯出來的結果是：

```plaintext
  0           RESUME                   0

  1           LOAD_NAME                0 (print)
              PUSH_NULL
              LOAD_CONST               0 (&#39;Hello&#39;)
              CALL                     1
              POP_TOP

  3           NOP

  9           LOAD_NAME                0 (print)
              PUSH_NULL
              LOAD_CONST               1 (&#39;World&#39;)
              CALL                     1
              POP_TOP
              RETURN_CONST             2 (None)
```

可以看到中間的多行文字直接消失了。

是說，如果把這個寫法放在類別或函數裡的第一行，可以造成 Docstring 的效果，也就是會幫類別或函數的 `__doc__` 屬性加料，但如果只是要做到 Docstring 的效果，一般的字串就能做到了，不一定需要用到多行字串。

Python 的發明人 Guido 曾經對多行註解這樣[回覆](https://twitter.com/gvanrossum/status/112670605505077248)過：

![](/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBbTBFIiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--56d5eeef430085b9f0d3cc6c48840eec61f3ead0/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCem9MWm05eWJXRjBTU0lJY0c1bkJqb0dSVlE2RkhKbGMybDZaVjkwYjE5c2FXMXBkRnNIYVFJQUJXa0NBQVE9IiwiZXhwIjpudWxsLCJwdXIiOiJ2YXJpYXRpb24ifX0=--578d6799c87a604ca574298502ba874c9075e929/multiline-string-as-comment.png)

如 Guido 所說，的確是可以拿多行文字來「當做」多行註解，但這同時也表示 Python 本身就是沒有多行註解的設計，如果有的話就不需要用多行文字來「當做」了。

## type() 不是函數

如果想要在 Python 裡印出某個資料的型別，可能會使用 `type()` 這個函數（或函式），如果想要做數字或字串轉型可能會用 `int()` 或 `str()` 函數，像這樣：

```plaintext
&gt;&gt;&gt; type(123)
&lt;class &#39;int&#39;&gt;

&gt;&gt;&gt; int(&quot;123&quot;)
123

&gt;&gt;&gt; str(123)
&#39;123&#39;
```

但是，事實上不管是 `int`、`str` 或是 `type`，它們在 Python 裡都是一個「類別（Class）」，只是剛好只傳一個引數給它的時候，它會做一些特別的事，例如做型別轉換或是印出它的型別，不信的話你可以用 `type()` 檢驗看看：

```plaintext
&gt;&gt;&gt; type(int)
&lt;class &#39;type&#39;&gt;
```

`int`、`str`、`list`、`dict` 等類別都是一種叫做 `type` 的型別，這是因為 `type` 型別在 Python 是所有內建類別的 meta class，也就是所有內建型別都是一種 `type`，包括 `type` 自己也是：

```plaintext
&gt;&gt;&gt; type(type)
&lt;class &#39;type&#39;&gt;
```

雖然它們用起來的手感像是呼叫函數，但在 Python 裡它們都是實實在在的類別 :)

## 關於布林值

在 Python 裡的布林型態（`bool`）的 `True` 跟 `False`，有些網路上的教學會說它們可以被轉型成數字 `1` 跟 `0`。這樣的說法沒問題，但可能比較少人知道在 Python 裡布林值其實就是一種數字。不信的話，可以試著這樣做：

```plaintext
&gt;&gt;&gt; bool.__base__
&lt;class &#39;int&#39;&gt;
```

`__base__` 屬性可以取得類別的上層類別，所以可以看的出來 `bool` 的上層類別就是數字 `int`，它們之間是有繼承關係的，所以布林值就是一種（kind of）整數。

而且，在 Python 裡的 `True` 跟 `False` 都只會存在一份，而且還是直接內建在 Python 直譯器裡。不管你怎麼建立新的布林值物件，它都會指向同一顆物件。例如：

```plaintext
&gt;&gt;&gt; a = bool(123)
&gt;&gt;&gt; b = bool(&quot;xyz&quot;)
&gt;&gt;&gt; a is b
True
&gt;&gt;&gt; a is True
True
```

可以看到它們就是同一顆物件。

另外，如果程式碼中使用了一看就知道的布林值判斷，例如：

```python
if True:
  print(&quot;這是真的&quot;)
```

因為這一定會發生，所以 Python 編譯的過程會直接把 `if True` 的判斷拿掉，只留下底下的程式碼。反之，如果是這樣寫：

```python
if False:
  print(&quot;這根本不會發生&quot;)
```

因為這一定不會發生，所以這段程式碼直接會被 Python 無視，根本不會被編譯成 Bytecode。

最後再補個冷知識，在 Python 2.2 之前，Python 其實是沒有布林型態的，早期的開發者常會使用 0 和 1 來代替布林值判斷，或是自己定義 `True` 和 `False`，例如 `True = 1` 或是 `False = 0` 這樣的寫法。直到 [PEP 285](https://peps.python.org/pep-0285/) 提案之後才正式引入布林值的設計，並將上層類別設定成 `int` 類別。不過，在 Pytohn 2 時代，就算 PEP 285 提案已經通過，`True` 跟 `False` 還是可以被重新定義：

```plaintext
# 注意，這是 Python 2
&gt;&gt;&gt; True = False
&gt;&gt;&gt; print(True)
False
```

直到 Python 3 之後，`True` 跟 `False` 才變成保留字，無法被重新定義。


