詳解 Python 中的底線命名規則

來源:互聯網
上載者:User

標籤:

在 python 中,底線命名規則往往令初學者相當疑惑:單底線、雙底線、雙底線還分前後……那它們的作用與使用情境到底有何區別呢?今天就來聊聊這個話題。

1、單底線(_) 通常情況下,單底線(_)會在以下3種情境中使用:
1.1 在解譯器中:

在這種情況下,“_”代表互動式解譯器會話中上一條執行的語句的結果。這種用法首先被標準CPython解譯器採用,然後其他類型的解譯器也先後採用。

>>> _ Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name ‘_‘ is not defined >>> 42>>> _ 42>>> ‘alright!‘ if _ else ‘:(‘‘alright!‘>>> _ ‘alright!‘
1.2 作為一個名稱:

這與上面一點稍微有些聯絡,此時“_”作為臨時性的名稱使用。這樣,當其他人閱讀你的代碼時將會知道,你分配了一個特定的名稱,但是並不會在後面再次用到該名稱。例如,下面的例子中,你可能對迴圈計數中的實際值並不感興趣,此時就可以使用“_”。

n = 42for _ in range(n):     do_something()
1.3 國際化:

也許你也曾看到”_“會被作為一個函數來使用。這種情況下,它通常用於實現國際化和本地化字串之間翻譯尋找的函數名稱,這似乎源自並遵循相應的C約定。例如,在Django文檔“轉換”章節中,你將能看到如下代碼:

from django.utils.translation import ugettext as _ from django.http import HttpResponse def my_view(request):     output = _("Welcome to my site.")     return HttpResponse(output)
可以發現,情境二和情境三中的使用方法可能會相互衝突,所以我們需要避免在使用“_”作為國際化尋找轉換功能的代碼塊中同時使用“_”作為臨時名稱。 2、名稱前的單底線(如:_shahriar) 程式員使用名稱前的單底線,用於指定該名稱屬性為“私人”。這有點類似於慣例,為了使其他人(或你自己)使用這些代碼時將會知道以“_”開頭的名稱只供內部使用。正如Python文檔中所述:
以底線“_”為首碼的名稱(如_spam)應該被視為API中非公開的部分(不管是函數、方法還是資料成員)。此時,應該將它們看作是一種實現細節,在修改它們時無需對外部通知。
正如上面所說,這確實類似一種慣例,因為它對解譯器來說確實有一定的意義,如果你寫了代碼“from <模組/包名> import *”,那麼以“_”開頭的名稱都不會被匯入,除非模組或包中的“__all__”列表顯式地包含了它們。瞭解更多請查看“ Importing * in Python ”。
不過值得注意的是,如果使用 import a_module 這樣匯入模組,仍然可以用 a_module._some_var 這樣的形式訪問到這樣的對象。

另外單底線開頭還有一種一般不會用到的情況在於使用一個 C 編寫的擴充庫有時會用底線開頭命名,然後使用一個去掉底線的 Python 模組進行封裝。如 struct 這個模組實際上是 C 模組 _struct 的一個 Python 封裝。

3、名稱前的雙底線(如:__shahriar) 名稱(具體為一個方法名)前雙底線(__)的用法並不是一種慣例,對解譯器來說它有特定的意義。Python中的這種用法是為了避免與子類定義的名稱衝突。Python文檔指出,“__spam”這種形式(至少兩個前置底線,最多一個後續底線)的任何標識符將會被“_classname__spam”這種形式原文取代,在這裡“classname”是去掉前置底線的當前類名。例如下面的例子:
>>> class A(object): ...     def _internal_use(self): ...         pass...     def __method_name(self): ...         pass... >>> dir(A()) [‘_A__method_name‘, ..., ‘_internal_use‘]
正如所預料的,“_internal_use”並未改變,而“__method_name”卻被變成了“_ClassName__method_name”:__開頭 的 私人變數會在代碼產生之前被轉換為長格式(變為公有)。轉換機制是這樣的:在變數前端插入類名,再在前端加入一個底線。這就是所謂的私人變數 名字改編 (Private name mangling) 。 此時,如果你建立A的一個子類B,那麼你將不能輕易地覆寫A中的方法“__method_name”,
>>> class B(A): ...     def __method_name(self): ...         pass... >>> dir(B()) [‘_A__method_name‘, ‘_B__method_name‘, ..., ‘_internal_use‘]
然而如果你知道了這個規律,最終你還是可以訪問這個“私人”變數的。
私人變數名字改編意在給出一個在類中定義"私人"執行個體變數和方法的簡單途徑,避免衍生類別的執行個體變數定義產生問題,或者與外界代碼中的變數搞混。
要注意的是混淆規則(私人變數名字改編)主要目的在於避免意外錯誤,被認作為私人的變數仍然有可能被訪問或修改(使用_classname__membername),在特定的場合它也是有用的,比如調試的時候。

上述的功能幾乎和Java中的final方法和C++類中標準方法(非虛方法)一樣。

再講兩點題外話:
一是因為軋壓(改編)會使標識符變長,當超過255的時候,Python會切斷,要注意因此引起的命名衝突。
二是當類名全部以底線命名的時候,Python就不再執行軋壓(改編)。

無論是單底線還是雙底線開頭的成員,都是希望外部程式開發人員不要直接使用這些成員變數和這些成員函數,只是雙底線從文法上能夠更直接的避免錯誤的使用,但是如果按照 _類名__成員名 則依然可以訪問到。單底線的在動態調試時可能會方便一些,只要項目組的人都遵守底線開頭的成員不直接使用,那使用單底線或許會更好。

4、名稱前後的雙底線(如:__init__) 這種用法表示Python中特殊的方法名。其實,這隻是一種慣例,對Python系統來說,這將確保不會與使用者自訂的名稱衝突。通常,你將會覆寫這些方法,並在裡面實現你所需要的功能,以便Python調用它們。例如,當定義一個類時,你經常會覆寫“__init__”方法。

雙底線開頭雙底線結尾的是一些 Python 的“魔術”對象,如類成員的 __init__、__del__、__add__、__getitem__ 等,以及全域的 __file__、__name__ 等。 Python 官方推薦永遠不要將這樣的命名方式應用於自己的變數或函數,而是按照文檔說明來使用。雖然你也可以編寫自己的特殊方法名,但不要這樣做。

>>> class C(object): ...     def __mine__(self): ...         pass... >>> dir(C) ... [..., ‘__mine__‘, ...]
其實,很容易擺脫這種類型的命名,而只讓Python內部定義的特殊名稱遵循這種約定 :) 5、題外話 if __name__ == "__main__":

所有的 Python 模組都是對象並且有幾個有用的屬性,你可以使用這些屬性方便地測試你所書寫的模組。

模組是對象, 並且所有的模組都有一個內建屬性 __name__。一個模組的 __name__ 的值要看您如何應用模組。如果 import 模組, 那麼 __name__的值通常為模組的檔案名稱, 不帶路徑或者副檔名。但是您也可以像一個標準的程式一樣直接運行模組, 在這種情況下 __name__的值將是一個特別的預設值:__main__。

>>> import odbchelper>>> odbchelper.__name__‘odbchelper‘

一旦瞭解到這一點, 您可以在模組內部為您的模組設計一個測試套件, 在其中加入這個 if 語句。當您直接運行模組, __name__ 的值是 __main__, 所以測試套件執行。當您匯入模組, __name__的值就是別的東西了, 所以測試套件被忽略。這樣使得在將新的模組整合到一個大程式之前開發和調試容易多了。
在 MacPython 上, 需要一個額外的步聚來使得 if __name__ 技巧有效。 點擊視窗右上方的黑色三角, 彈出模組的屬性菜單, 確認 Run as __main__ 被選中。

6、總結:

Python 用底線作為變數首碼和尾碼指定特殊變數。
_xxx       不能用‘from module import *‘匯入
__xxx__  系統定義名字
__xxx     類中的私人變數名
核心風格:避免用底線作為變數名的開頭。
因為底線對解譯器有特殊的意義,而且是內建標識符所使用的符號,我們建議程式員避免用底線作為變數名的開頭。一般來講,變數名_xxx被看作是“私人的”,在模組或類外不可以使用。當變數是私人的時候,用_xxx 來表示變數是很好的習慣。因為變數名__xxx__對Python 來說有特殊含義,對於普通的變數應當避免這種命名風格。
"單底線" 開始的成員變數叫做保護變數,意思是只有類對象和子類對象自己能訪問到這些變數;
"雙底線" 開始的是私人成員,意思是只有類對象自己能訪問,連子類對象也不能訪問到這個資料。
以單底線開頭(如_foo)的代表不能直接存取的類屬性,需通過類提供的介面進行訪問,不能用“from xxx import *”而匯入;以雙底線開頭的(如__foo)代表類的私人成員;以雙底線開頭和結尾的(__foo__)代表python裡特殊方法專用的標識,如 __init__() 代表類的建構函式。

附 PEP 規範:

PEP-0008:In addition, the following special forms using leading or trailing underscores are recognized (these can generally be combined with any case convention):    - _single_leading_underscore: weak "internal use" indicator. E.g. "from M import *" does not import objects whose name starts with an underscore.    - single_trailing_underscore_: used by convention to avoid conflicts with Python keyword, e.g.      Tkinter.Toplevel(master, class_=‘ClassName‘)    - __double_leading_underscore: when naming a class attribute, invokes name mangling (inside class FooBar, __boo becomes _FooBar__boo; see below).    - __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.
7、Refer:

[1] Importing `*` in Python

http://shahriar.svbtle.com/importing-star-in-python

[2] 理解Python的雙底線命名

http://blog.csdn.net/zhu_liangwei/article/details/7667745

[3] Python 的類的底線命名有什麼不同?

http://www.zhihu.com/question/19754941

詳解 Python 中的底線命名規則

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.