Python中的下劃線
018.11.25
前言
我發現公司裏即便是工作經驗一年的程序員依然對Python的私有屬性有些迷糊——也就是下劃線的問題——拿捏不準要怎麼用。我想在此總結一下。
單下劃線
前置
前置單下劃線是一種約定,即:認爲這個變量或方法是類私有,外界不要調用。在這裏,對象認爲是“外界”。
示例如下:
class Persion(object):
def __init__(self, name, age, hobbies):
# 不建議被外界使用
self._name = name
self._age = age
self._hobbies = hobbies
# 允許被外界調用
@property
def detail(self):
return self._get_sentence()
# 建議不要被外界調用,而是在類中使用
def _get_sentence(self):
return f"{self._name} is {self._age} and loves {self._hobbies}."
if __name__ == "__main__":
lily = Persion("lily", 18, "listenning")
print(lily.detail)
# 輸出:
# lily is 18 and loves listenning.
但外界能不能調用帶前置單下劃線的成員變量和成員方法呢?事實上是可以的:
print(lily._name) # 輸出:lily
print(lily._get_sentence()) # 輸出: lily is 18 and loves listening.
我實在記不清是《Python編程之美》還是《數據結構(Python描述)》,裏面對此解釋說:之所以Python對類私有隻是一種約定,是認爲使用Python開發的程序員應該是成熟的。
而另一方面,在一個模塊中,前置單下劃線的變量或是函數,不能通過from module import *
方式導入。如:
# demo1.py
# 這是一個變量
_one = "ONE"
# 這是一個函數
def _two():
print("TWO")
# demo2.py
from demo1 import *
print(_one) # 錯誤使用
_two() # 錯誤使用
對此解釋器會拋出異常:NameError: name 'xxx' is not defined
。
但並非demo1中的前置單下劃線變量/方法在demo2中就不可以使用,完全可以import module
,然後通過module.xxx
方式,demo2代碼做如下調整:
# demo2.py
import demo1
print(demo1._one) # 正確使用
demo1._two() # 正確使用
後置
後置單下劃線是一種編程風格建議,即:當你的變量/方法名與Python中的關鍵字衝突時,可以加上後置單下劃線做區分。如:
list_ = ["lily", "bob", "green"]
dict_ = {
"name": "lily",
"age": 18
}
雙下劃線
前置
事實上,前置雙下劃線變量/方法,更像是類私有。將第一個示例代碼改寫如下(單下劃線都改寫爲雙下劃線):
class Persion(object):
def __init__(self, name, age, hobbies):
# 不能被外界訪問
self.__name = name
self.__age = age
self.__hobbies = hobbies
# 允許被外界調用
@property
def detail(self):
return self.__get_sentence()
# 不能被外界調用
def __get_sentence(self):
return f"{self.__name} is {self.__age} and loves {self.__hobbies}."
if __name__ == "__main__":
lily = Persion("lily", 18, "listening")
print(lily.detail)
# 輸出:
# lily is 18 and loves listening.
可如果你想外界訪問:
print(lily.__name) # 錯誤示例
print(lily.__get_sentence()) # 錯誤示例
解釋器會拋異常:AttributeError: 'Persion' object has no attribute '__name'
。
其實同前置單下劃線一樣,雙下劃線其實也可以被外界訪問,只不過解釋器對他們**“以類之姓,冠它之名”**。
print(lily._Persion__name) # 輸出:lily
lily._Persion__get_sentence() # 輸出:lily is 18 and loves listening.
也就是說,當類名爲X
,雙下劃線變量爲__y
時,外界對其訪問應該形如:_X__y
。
完全與前置單下劃線一樣的是,模塊中的前置雙下劃線變量/函數,不能通過from module import *
導入,但可以import module
後使用:
module.__one
module.__two()
前置雙下劃線存在的意義:爲了避免該成員的名稱與子類中的名稱衝突。
後置
開個玩笑。
後置雙下劃線既沒有實際意義,也不是編程風格的建議。
前後置
稍微對Python熟悉的人就會知道,存在前置雙下劃線同時還有後置雙下劃線的變量以及方法。如常見的__name__
以及__init__()
。這些屬於Python的魔法對象,是爲了區別其他用戶自定義的命名。官方表示:永遠不要將這樣的命名方式應用於自己的變量或函數。
感謝
- 參考GitHuber taizilongxu https://github.com/taizilongxu/interview_python
- 參考知乎er 權循真 https://www.zhihu.com/question/19754941