---
# System prepended metadata

title: Underscore( _ ) of Python
tags: [Python]

---

---
tags: Python
---
# Underscore( _ ) of Python

[TOC]

The underscore (_) is special in Python.

## Cases for using the underscore

### For storing the value of last expression in interpreter.

The python interpreter stores **the last expression value** to the special variable called ‘_’.

```python
>>> 10
10
>>> _
10
>>> _ * 3
30
>>> _
30
```

如果給值了，這個功能就失效。

```python
>>> _ = 3
>>> 10
10
>>> _
3
```

### For ignoring the specific values. (so-called “I don’t care”)

```python
# Ignore a value when unpacking
x, _, y = (1, 2, 3)  # x = 1, y = 3

# Ignore the multiple values. It is called "Extended Unpacking" which is available in only Python 3.x
x, *_, y = (1, 2, 3, 4, 5)  # x = 1, y = 5  

# Ignore the index
for _ in range(10):
    do_something()

# Ignore a value of specific location
for _, val in list_of_tuple:
    do_something()
```

當然你也可以不用 `_` ，也可以達到一樣的效果。

### To give special meanings and functions to name of vartiables or functions.

[PEP8](https://pep8.org/#descriptive-naming-styles) 介紹了四種特別的命名格式。

#### `_single_leading_underscore`

weak “internal use” indicator.

表示內部使用。當外部 `from M import *` 時不會被包含在內。

> “Private” instance variables that cannot be accessed except from inside an object don’t exist in Python. However, there is a convention that is followed by most Python code: a name prefixed with an underscore (e.g. _spam) should be treated as a non-public part of the API (whether it is a function, a method or a data member).

當然 Python 不支援真的 private ，所以還是可以直接呼叫。

```python
import M
M._func()
```

1. 並不是 Private variable
2. 不需要 docstrings `lint: D103: Missing docstring in public function`

#### `single_trailing_underscore_`

避免和 Python built-in keywords 衝突。

```python
Tkinter.Toplevel(master, class_='ClassName')
```

但這種命名其實不夠精確，可以考慮更好的選擇，例如： `level_class` 。

#### `__double_leading_underscore`

```python
class A:
    def _single_method(self):
        pass
    def __double_method(self): # for mangling
        pass
class B(A):
    def __double_method(self): # for mangling
        pass

>>> import M
>>> dir(M.A)
['_A__double_method', '__doc__', '__module__', '_single_method']
>>> dir(M.B)
['_A__double_method', '_B__double_method', '__doc__', '__module__', '_single_method']
```

這種命名方法會 trigger 所謂的 name mangling ，避免產生命名碰撞，因為這樣可能會導致被繼承的父類裡面定義的其他 method 運作不正常。

[舉個例](https://aji.tw/python%E4%BD%A0%E5%88%B0%E5%BA%95%E6%98%AF%E5%9C%A8__%E5%BA%95%E7%B7%9A__%E4%BB%80%E9%BA%BC%E5%95%A6/)：
 
我有一個好朋友，叫做 `Jason` :man:，他長的像這樣：

```python
class Jason:

    location = 'HsinChu'
    hobby = 'sleeping'
    __wife = 'Mary'

    def profile(self):
        """Print my personal profile."""
        print(f'''
            I live in {self.location}
            My hobby is {self.hobby}
            My wife is {self.__wife}
        ''')
```

> [PEP498 Literal String Interpolation](https://www.python.org/dev/peps/pep-0498/) f-string 是 Python3.6 引入的新方法。
> 相較於 `format` 更簡潔：
> 
> ```python
> 'Hello, {name}. You are {age}.'.format(name=name, age=age)
> ```
> 
> ```python
> f'Hello, {name}. You are {age}.'
> ```
> 
> 效能也更高，比 format 快了一倍：F-strings provide a way to embed expressions inside string literals, using a minimal syntax. It should be noted that an f-string is really an expression evaluated at run time, not a constant value. In Python source code, an f-string is a literal string, prefixed with 'f', which contains expressions inside braces. The expressions are replaced with their values.
> 
> pylint 需要升級到 2.x ，否則會被視為 `syntax-error` 。
> expression 內變數未定義時， pylint 能夠正確抓到錯誤：`undefined-variable` 。

我們來看看他的個人檔案會長什麼樣子：

```python
>>> Jason().profile()
I live in HsinChu
My hobby is sleeping
My wife is Mary
```

我是他的麻吉罵，而且跟他有很多共通點，所以我直接繼承他。不過我跟他住的地方不一樣，老婆也不一樣。

```
class Aji(Jason):
    location = 'Taipei'
    __wife = 'Boa'
```

那來看看我的個人檔案會長什麼樣子：

```python
>>> Aji().profile()
I live in Taipei
My hobby is sleeping
My wife is Mary
```

等等，我的老婆是 Boa 不是 Mary 啊！朋友妻不可戲啊 :scream:！
另外，別人的老婆也不是隨隨便便可以得的。

```python
>>> jason = Jason()
>>> jason.location
HsinChu
>>> jason.__wife
AttributeError: 'Jason' object has no attribute '__wife'
```

不過透過特殊的方法，~~還是可以取得 Jason 的老婆的~~。

```python
>>> jason._Jason__wife
'Mary'
```

#### `__double_leading_and_trailing_underscore__`

> __double_leading_and_trailing_underscore__ : "magic" objects or attributes that live in user-controlled namespaces. E.g. __init__ , __import__ or __file__ . Never invent such names; only use them as documented.

Python 都叫你不要自己發明了，就饒過自己吧。

~~鼎鼎大名的 [SQLAlchemy](https://github.com/zzzeek/sqlalchemy) 用了一堆像是 `__tablename__`, `__table__`, `__mapper__` 等等的東西，還沒爆炸但不知道會不會爆炸。~~


### To use as ‘Internationalization(i18n)’ or ‘Localization(L10n)’ functions.

It is just convention, does not have any syntactic functions.

國際化應用程式是一種使軟體可以適應各種語言的一種開發過程，而不需要工程上的更動。

* **i18n internationalization 國際化**：18 意味著在 internationalization 這個單字中，i 和 n 之間有 18 個字母。當軟體被移植到不同的語言及地區時，軟體本身不用做內部工程上的改變或修正。
* **L10n localization 在地化**：使用大寫的 L 以利區分 i18n 中的 i 和易於分辨小寫 l 與 1 。當移植軟體時，加上與特定區域設定有關的資訊和翻譯檔案的過程。

```python
# see official docs : https://docs.python.org/3/library/gettext.html
import gettext
gettext.bindtextdomain('myapplication','/path/to/my/language/directory')
gettext.textdomain('myapplication')
_ = gettext.gettext
# ...
print(_('This is a translatable string.'))
```

如果用這個方法然後還用到 ignoring the specific value 的功能，比較多的人會使用 `__` 雙底線來代表 ignore 。

~~這邊實際法我還沒看~~

### To separate the digits of number literal value.

增加可讀性 :thumbsup: 。

```python
dec_base = 1_000_000
bin_base = 0b_1111_0000
hex_base = 0x_1234_abcd
print(dec_base) # 1000000
print(bin_base) # 240
print(hex_base) # 305441741
```

## 資料來源

1. [Understanding the underscore( _ ) of Pythontra](https://medium.com/hackernoon/understanding-the-underscore-of-python-309d1a029edc)
2. [Python，你到底是在__底線__什麼啦！](https://aji.tw/python%E4%BD%A0%E5%88%B0%E5%BA%95%E6%98%AF%E5%9C%A8__%E5%BA%95%E7%B7%9A__%E4%BB%80%E9%BA%BC%E5%95%A6/)
3. [Python 3's f-Strings: An Improved String Formatting Syntax (Guide)](https://realpython.com/python-f-strings/)