--- 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/)