python 相關(guān)規(guī)范 (轉(zhuǎn))

PEP8

縮進(jìn)

每一行使用四個(gè)空格
續(xù)行應(yīng)該與其包裹元素對(duì)齊,要么使用圓括號(hào)、方括號(hào)和花括號(hào)內(nèi)的隱式行連接來(lái)垂直對(duì)齊,要么使用掛行縮進(jìn)對(duì)齊。當(dāng)使用掛行縮進(jìn)時(shí),應(yīng)該考慮到第一行不應(yīng)該有參數(shù),以及使用縮進(jìn)以區(qū)分自己是續(xù)行。
推薦寫(xiě)法:

# 與左括號(hào)對(duì)齊
foo = long_function_name(var_one, var_two,
                         var_three, var_four)

# 用更多的縮進(jìn)來(lái)與其他行區(qū)分
def long_function_name(
        var_one, var_two, var_three,
        var_four):
    print(var_one)

# 掛行縮進(jìn)應(yīng)該再換一行
foo = long_function_name(
    var_one, var_two,
    var_three, var_four)

不推薦:

# 沒(méi)有使用垂直對(duì)齊時(shí),禁止把參數(shù)放在第一行
foo = long_function_name(var_one, var_two,
    var_three, var_four)

# 當(dāng)縮進(jìn)沒(méi)有與其他行區(qū)分時(shí),要增加縮進(jìn)
def long_function_name(
    var_one, var_two, var_three,
    var_four):
    print(var_one)

四空格的規(guī)則對(duì)于續(xù)行是可選的

# 掛行縮進(jìn)不一定要用4個(gè)空格
foo = long_function_name(
  var_one, var_two,
  var_three, var_four)

當(dāng)if語(yǔ)句的條件部分長(zhǎng)到需要換行寫(xiě)的時(shí)候,注意可以在兩個(gè)字符關(guān)鍵字的連接處(比如if),增加一個(gè)空格,再增加一個(gè)左括號(hào)來(lái)創(chuàng)造一個(gè)4空格縮進(jìn)的多行條件。這會(huì)與if語(yǔ)句內(nèi)同樣使用4空格縮進(jìn)的代碼產(chǎn)生視覺(jué)沖突。PEP沒(méi)有明確指明要如何區(qū)分i發(fā)的條件代碼和內(nèi)嵌代碼??墒褂玫倪x項(xiàng)包括但不限于下面幾種情況:

# 沒(méi)有額外的縮進(jìn)
if (this_is_one_thing and
    that_is_another_thing):
    do_something()

# 增加一個(gè)注釋?zhuān)谀芴峁┱Z(yǔ)法高亮的編輯器中可以有一些區(qū)分
if (this_is_one_thing and
    that_is_another_thing):
    # Since both conditions are true, we can frobnicate.
    do_something()

# 在條件判斷的語(yǔ)句添加額外的縮進(jìn)
if (this_is_one_thing
        and that_is_another_thing):
    do_something()

(可以參考下面關(guān)于是否在二進(jìn)制運(yùn)算符之前或之后截?cái)嗟挠懻摚?br> 在多行結(jié)構(gòu)中的大括號(hào)/中括號(hào)/小括號(hào)的右括號(hào)可以與內(nèi)容對(duì)齊單獨(dú)起一行作為最后一行的第一個(gè)字符,就像這樣:

my_list = [
    1, 2, 3,
    4, 5, 6,
    ]
result = some_function_that_takes_arguments(
    'a', 'b', 'c',
    'd', 'e', 'f',
    )

或者也可以與多行結(jié)構(gòu)的第一行第一個(gè)字符對(duì)齊,就像這樣:

my_list = [
    1, 2, 3,
    4, 5, 6,
]
result = some_function_that_takes_arguments(
    'a', 'b', 'c',
    'd', 'e', 'f',
)

制表符或者空格縮進(jìn)

空格是首選的縮進(jìn)方式。
制表符只能用于與同樣使用制表符縮進(jìn)的代碼保持一致。
Python3不允許同時(shí)使用空格和制表符的縮進(jìn)。
混合使用制表符和空格縮進(jìn)的Python2代碼應(yīng)該統(tǒng)一轉(zhuǎn)成空格。
當(dāng)在命令行加入-t選項(xiàng)執(zhí)行Python2時(shí),它會(huì)發(fā)出關(guān)于非法混用制表符與空格的警告。當(dāng)使用–tt時(shí),這些警告會(huì)變成錯(cuò)誤。強(qiáng)烈建議使用這樣的參數(shù)

行的最大長(zhǎng)度

所有行限制的最大字符數(shù)為79。
沒(méi)有結(jié)構(gòu)化限制的大塊文本(文檔字符或者注釋?zhuān)啃械淖畲笞址麛?shù)限制在72。
限制編輯器窗口寬度可以使多個(gè)文件并行打開(kāi),并且在使用代碼檢查工具(在相鄰列中顯示這兩個(gè)版本)時(shí)工作得很好。
大多數(shù)工具中的默認(rèn)封裝破壞了代碼的可視化結(jié)構(gòu),使代碼更難以理解。避免使用編輯器中默認(rèn)配置的80窗口寬度,即使工具在幫你折行時(shí)在最后一列放了一個(gè)標(biāo)記符。某些基于Web的工具可能根本不提供動(dòng)態(tài)折行。
一些團(tuán)隊(duì)更喜歡較長(zhǎng)的行寬。如果代碼主要由一個(gè)團(tuán)隊(duì)維護(hù),那這個(gè)問(wèn)題就能達(dá)成一致,可以把行長(zhǎng)度從80增加到100個(gè)字符(更有效的做法是將行最大長(zhǎng)度增加到99個(gè)字符),前提是注釋和文檔字符串依然已72字符折行。
Python標(biāo)準(zhǔn)庫(kù)比較保守,需要將行寬限制在79個(gè)字符(文檔/注釋限制在72)。
較長(zhǎng)的代碼行選擇Python在小括號(hào),中括號(hào)以及大括號(hào)中的隱式續(xù)行方式。通過(guò)小括號(hào)內(nèi)表達(dá)式的換行方式將長(zhǎng)串折成多行。這種方式應(yīng)該優(yōu)先使用,而不是使用反斜杠續(xù)行。
反斜杠有時(shí)依然很有用。比如,比較長(zhǎng)的,多個(gè)with狀態(tài)語(yǔ)句,不能使用隱式續(xù)行,所以反斜杠是可以接受的:

with open('/path/to/some/file/you/want/to/read') as file_1, \
     open('/path/to/some/file/being/written', 'w') as file_2:
    file_2.write(file_1.read())

另一種類(lèi)似情況是使用assert語(yǔ)句。
確保在續(xù)行進(jìn)行適當(dāng)?shù)目s進(jìn)。

在二元運(yùn)算符前應(yīng)該換行嗎?

幾十年來(lái),推薦的風(fēng)格是在二元運(yùn)算符之后中斷。但是這回影響可讀性,原因有二:操作符一般分布在屏幕上不同的列中,而且每個(gè)運(yùn)算符被移到了操作數(shù)的上一行。下面例子這個(gè)情況就需要額外注意,那些變量是相加的,那些變量是相減的:

# 不推薦: 操作符離操作數(shù)太遠(yuǎn)
income = (gross_wages +
          taxable_interest +
          (dividends - qualified_dividends) -
          ira_deduction -
          student_loan_interest)

為了解決這種可讀性的問(wèn)題,數(shù)學(xué)家和他們的出版商遵循了相反的約定。Donald Knuth在他的Computers and Typesetting系列中解釋了傳統(tǒng)規(guī)則:“盡管段落中的公式總是在二元運(yùn)算符和關(guān)系之后中斷,顯示出來(lái)的公式總是要在二元運(yùn)算符之前中斷。
遵循數(shù)學(xué)的傳統(tǒng)能產(chǎn)出更多可讀性高的代碼:

# 推薦:運(yùn)算符和操作數(shù)很容易進(jìn)行匹配
income = (gross_wages
          + taxable_interest
          + (dividends - qualified_dividends)
          - ira_deduction
          - student_loan_interest)

在Python代碼中,允許在二元運(yùn)算符之前或之后中斷,只要本地的約定是一致的。對(duì)于新代碼,建議使用Knuth的樣式。

空行

頂層函數(shù)和類(lèi)的定義,前后用兩個(gè)空行隔開(kāi)。
類(lèi)里的方法定義用一個(gè)空行隔開(kāi)。
相關(guān)的功能組可以用額外的空行(謹(jǐn)慎使用)隔開(kāi)。一堆相關(guān)的單行代碼之間的空白行可以省略(例如,一組虛擬實(shí)現(xiàn) dummy implementations)。
在函數(shù)中使用空行來(lái)區(qū)分邏輯段(謹(jǐn)慎使用)。
Python接受control-L(即^L)換頁(yè)符作為空格;許多工具把這些字符當(dāng)作頁(yè)面分隔符,所以你可以在文件中使用它們來(lái)分隔相關(guān)段落。請(qǐng)注意,一些編輯器和基于Web的代碼閱讀器可能無(wú)法識(shí)別control-L為換頁(yè),將在其位置顯示另一個(gè)字形。

import sys
import os


class Car(object): # 類(lèi)使用兩行空行
   
   def __init__(self): # 方法使用一行空行
       self.color = color
       xxxxx

源文件編碼

Python核心發(fā)布版本中的代碼總是以UTF-8格式編碼(或者在Python2中用ASCII編碼)。
使用ASCII(在Python2中)或UTF-8(在Python3中)編碼的文件不應(yīng)具有編碼聲明。
在標(biāo)準(zhǔn)庫(kù)中,非默認(rèn)的編碼應(yīng)該只用于測(cè)試,或者當(dāng)一個(gè)注釋或者文檔字符串需要提及一個(gè)包含內(nèi)ASCII字符編碼的作者名字的時(shí)候;否則,使用\x,\u,\U , 或者 \N 進(jìn)行轉(zhuǎn)義來(lái)包含非ASCII字符。
對(duì)于Python 3和更高版本,標(biāo)準(zhǔn)庫(kù)規(guī)定了以下策略(參見(jiàn) PEP 3131):Python標(biāo)準(zhǔn)庫(kù)中的所有標(biāo)識(shí)符必須使用ASCII標(biāo)識(shí)符,并在可行的情況下使用英語(yǔ)單詞(在許多情況下,縮寫(xiě)和技術(shù)術(shù)語(yǔ)是非英語(yǔ)的)。此外,字符串文字和注釋也必須是ASCII。唯一的例外是(a)測(cè)試非ASCII特征的測(cè)試用例,以及(b)作者的名稱(chēng)。作者的名字如果不使用拉丁字母拼寫(xiě),必須提供一個(gè)拉丁字母的音譯。
鼓勵(lì)具有全球受眾的開(kāi)放源碼項(xiàng)目采取類(lèi)似的政策。

導(dǎo)入

  • 導(dǎo)入通常分開(kāi)行導(dǎo)入
推薦: import os
     import sys

不推薦:  import sys, os

但是可以這樣導(dǎo)入

from subprocess import Popen, PIPE
  • 導(dǎo)入總是位于文件的頂部,在模塊注釋和文檔字符串之后,在模塊的全局變量與常量之前。
    導(dǎo)入應(yīng)該按照以下順序分組:
  1. 標(biāo)準(zhǔn)庫(kù)導(dǎo)入
  2. 相關(guān)第三方庫(kù)導(dǎo)入
  3. 本地應(yīng)用/庫(kù)特定導(dǎo)入
  4. 你應(yīng)該在每一組導(dǎo)入之間加入空行。
  • 推薦使用絕對(duì)路徑導(dǎo)入,如果導(dǎo)入系統(tǒng)沒(méi)有正確的配置(比如包里的一個(gè)目錄在sys.path里的路徑后),使用絕對(duì)路徑會(huì)更加可讀并且性能更好(至少能提供更好的錯(cuò)誤信息):
import mypkg.sibling
from mypkg import sibling
from mypkg.sibling import example

然而,顯示的指定相對(duì)導(dǎo)入路徑是使用絕對(duì)路徑的一個(gè)可接受的替代方案,特別是在處理使用絕對(duì)路徑導(dǎo)入不必要冗長(zhǎng)的復(fù)雜包布局時(shí):

from . import sibling
from .sibling import example

標(biāo)準(zhǔn)庫(kù)要避免使用復(fù)雜的包引入結(jié)構(gòu),而總是使用絕對(duì)路徑。
不應(yīng)該使用隱式相對(duì)路徑導(dǎo)入,并且在Python 3中刪除了它。

  • 當(dāng)從一個(gè)包含類(lèi)的模塊中導(dǎo)入類(lèi)時(shí),常常這么寫(xiě):
from myclass import MyClass
from foo.bar.yourclass import YourClass

如果上述的寫(xiě)法導(dǎo)致名字的沖突,那么這么寫(xiě):

import myclass
import foo.bar.yourclass

然后使用“myclass.MyClass”和“foo.bar.yourclass.YourClass”。

  • 避免通配符的導(dǎo)入(from import *),因?yàn)檫@樣做會(huì)不知道命名空間中存在哪些名字,會(huì)使得讀取接口和許多自動(dòng)化工具之間產(chǎn)生混淆。對(duì)于通配符的導(dǎo)入,有一個(gè)防御性的做法,即將內(nèi)部接口重新發(fā)布為公共API的一部分(例如,用可選加速器模塊的定義覆蓋純Python實(shí)現(xiàn)的接口,以及重寫(xiě)那些事先不知道的定義)。
    當(dāng)以這種方式重新發(fā)布名稱(chēng)時(shí),以下關(guān)于公共和內(nèi)部接口的準(zhǔn)則仍然適用。

模塊級(jí)的雙下劃線名字

__all__,__author__,__version__等這樣的名字應(yīng)該放在文檔字符串的后面以及除了from __future__之外的import表達(dá)式前面。python要求將來(lái)在模塊中的導(dǎo)入必須出現(xiàn)在除文檔字符串之外的其他代碼之前。比如:

"""This is the example module.

This module does stuff.
"""

from __future__ import barry_as_FLUFL

__all__ = ['a', 'b', 'c']
__version__ = '0.1'
__author__ = 'Cardinal Biggles'

import os
import sys

字符串引號(hào)

在Python中,單引號(hào)和雙引號(hào)字符串是相同的。PEP不會(huì)為這個(gè)給出建議。選擇一條規(guī)則并堅(jiān)持使用下去。當(dāng)一個(gè)字符串中包含單引號(hào)或者雙引號(hào)字符的時(shí)候,使用和最外層不同的符號(hào)來(lái)避免使用反斜杠,從而提高可讀性。
對(duì)于三引號(hào)字符串,總是使用雙引號(hào)字符來(lái)與PEP 257中的文檔字符串約定保持一致。

表達(dá)式和語(yǔ)句中的空格

在下列情況下,避免使用無(wú)關(guān)的空格:

  • 緊跟在小括號(hào),中括號(hào)或者大括號(hào)后。
Yes: spam(ham[1], {eggs: 2})
No:  spam( ham[ 1 ], { eggs: 2 } )
  • 緊貼在逗號(hào)、分號(hào)或者冒號(hào)之前
Yes: if x == 4: print x, y; x, y = y, x
No:  if x == 4 : print x , y ; x , y = y , x
  • 然而,冒號(hào)在切片中就像二元運(yùn)算符,在兩邊應(yīng)該有相同數(shù)量的空格(把它當(dāng)做優(yōu)先級(jí)最低的操作符)。在擴(kuò)展的切片操作中,所有的冒號(hào)必須有相同的間距。例外情況:當(dāng)一個(gè)切片參數(shù)被省略時(shí),空格就被省略了。
    推薦:
ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:]
ham[lower:upper], ham[lower:upper:], ham[lower::step]
ham[lower+offset : upper+offset]
ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]
ham[lower + offset : upper + offset]

不推薦:

ham[lower + offset:upper + offset]
ham[1: 9], ham[1 :9], ham[1:9 :3]
ham[lower : : upper]
ham[ : upper]
  • 緊貼在函數(shù)參數(shù)的左括號(hào)之前。
Yes: spam(1)
No:  spam (1)
  • 緊貼索引或者切片的左括號(hào)之前。
Yes: dct['key'] = lst[index]
No:  dct ['key'] = lst [index]
  • 為了和另一個(gè)賦值語(yǔ)句對(duì)齊,在賦值運(yùn)算符附近加多個(gè)空格。
    推薦:
x = 1
y = 2
long_variable = 3

不推薦:

x             = 1
y             = 2
long_variable = 3

其他建議

  • 避免在尾部添加空格。因?yàn)槲膊康目崭裢ǔ6伎床灰?jiàn),會(huì)產(chǎn)生混亂:比如,一個(gè)反斜杠后面跟一個(gè)空格的換行符,不算續(xù)行標(biāo)記。有些編輯器不會(huì)保留尾空格,并且很多項(xiàng)目(像CPython)在pre-commit的掛鉤調(diào)用中會(huì)過(guò)濾掉尾空格。
  • 總是在二元運(yùn)算符兩邊加一個(gè)空格:賦值(=),增量賦值(+=,-=),比較(==,<,>,!=,<>,<=,>=,in,not,in,is,is not),布爾(and, or, not)。
  • 如果使用具有不同優(yōu)先級(jí)的運(yùn)算符,請(qǐng)考慮在具有最低優(yōu)先級(jí)的運(yùn)算符周?chē)砑涌崭?。有時(shí)需要通過(guò)自己來(lái)判斷;但是,不要使用一個(gè)以上的空格,并且在二元運(yùn)算符的兩邊使用相同數(shù)量的空格。
    推薦:
i = i + 1
submitted += 1
x = x*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)

不推薦:

i=i+1
submitted +=1
x = x * 2 - 1
hypot2 = x * x + y * y
c = (a + b) * (a - b)
  • 在制定關(guān)鍵字參數(shù)或者默認(rèn)參數(shù)值的時(shí)候,不要在=附近加上空格。
    推薦:
def complex(real, imag=0.0):
    return magic(r=real, i=imag)

不推薦:

def complex(real, imag = 0.0):
    return magic(r = real, i = imag)
  • 功能型注釋?xiě)?yīng)該使用冒號(hào)的一般性規(guī)則,并且在使用->的時(shí)候要在兩邊加空格。(參考下面的功能注釋得到能夠多信息)
    推薦:
def munge(input: AnyStr): ...
def munge() -> AnyStr: ...

不推薦:

def munge(input:AnyStr): ...
def munge()->PosInt: ...
  • 當(dāng)給有類(lèi)型備注的參數(shù)賦值的時(shí)候,在=兩邊添加空格(僅針對(duì)那種有類(lèi)型備注和默認(rèn)值的參數(shù))。
    推薦:
def munge(sep: AnyStr = None): ...
def munge(input: AnyStr, sep: AnyStr = None, limit=1000): ...

不推薦:

ef munge(input: AnyStr=None): ...
def munge(input: AnyStr, limit = 1000): ...
  • 復(fù)合語(yǔ)句(同一行中的多個(gè)語(yǔ)句)通常是不允許的。
    推薦:
if foo == 'blah':
    do_blah_thing()
do_one()
do_two()
do_three()

不推薦:

if foo == 'blah': do_blah_thing()
do_one(); do_two(); do_three()
  • 雖然有時(shí)候?qū)⑿〉拇a塊和 if/for/while 放在同一行沒(méi)什么問(wèn)題,多行語(yǔ)句塊的情況不要這樣用,同樣也要避免代碼行太長(zhǎng)!
    最好別這樣:
if foo == 'blah': do_blah_thing()
for x in lst: total += x
while t < 10: t = delay()

也不推薦這樣:

if foo == 'blah': do_blah_thing()
else: do_non_blah_thing()

try: something()
finally: cleanup()

do_one(); do_two(); do_three(long, argument,
                             list, like, this)

if foo == 'blah': one(); two(); three()

注釋

與代碼相矛盾的注釋比沒(méi)有注釋還糟,當(dāng)代碼更改時(shí),優(yōu)先更新對(duì)應(yīng)的注釋?zhuān)?br> 注釋?xiě)?yīng)該是完整的句子。如果一個(gè)注釋是一個(gè)短語(yǔ)或句子,它的第一個(gè)單詞應(yīng)該大寫(xiě),除非它是以小寫(xiě)字母開(kāi)頭的標(biāo)識(shí)符(永遠(yuǎn)不要改變標(biāo)識(shí)符的大小寫(xiě)!)。
如果注釋很短,結(jié)尾的句號(hào)可以省略。塊注釋一般由完整句子的一個(gè)或多個(gè)段落組成,并且每句話結(jié)束有個(gè)句號(hào)。
在句尾結(jié)束的時(shí)候應(yīng)該使用兩個(gè)空格。
當(dāng)用英文書(shū)寫(xiě)時(shí),遵循Strunk and White (譯注:《Strunk and White, The Elements of Style》)的書(shū)寫(xiě)風(fēng)格。
在非英語(yǔ)國(guó)家的Python程序員,請(qǐng)使用英文寫(xiě)注釋?zhuān)悄?20%的確信你的代碼不會(huì)被使用其他語(yǔ)言的人閱讀。

塊注釋

塊注釋通常適用于跟隨它們的某些(或全部)代碼,并縮進(jìn)到與代碼相同的級(jí)別。塊注釋的每一行開(kāi)頭使用一個(gè)#和一個(gè)空格(除非塊注釋內(nèi)部縮進(jìn)文本)。
塊注釋內(nèi)部的段落通過(guò)只有一個(gè)#的空行分隔。

行內(nèi)注釋

有節(jié)制地使用行內(nèi)注釋。
行內(nèi)注釋是與代碼語(yǔ)句同行的注釋。行內(nèi)注釋和代碼至少要有兩個(gè)空格分隔。注釋由#和一個(gè)空格開(kāi)始。
事實(shí)上,如果狀態(tài)明顯的話,行內(nèi)注釋是不必要的,反而會(huì)分散注意力。比如說(shuō)下面這樣就不需要:

x = x + 1                 # x 值自增

但有時(shí),這樣做很有用:

x = x + 1                 # 擴(kuò)充邊界

文檔字符串

編寫(xiě)好的文檔說(shuō)明(也叫“docstrings”)的約定在PEP 257中永恒不變。

  • 要為所有的公共模塊,函數(shù),類(lèi)以及方法編寫(xiě)文檔說(shuō)明。非公共的方法沒(méi)有必要,但是應(yīng)該有一個(gè)描述方法具體作用的注釋。這個(gè)注釋?xiě)?yīng)該在def那一行之后。
  • PEP 257 描述了寫(xiě)出好的文檔說(shuō)明相關(guān)的約定。特別需要注意的是,多行文檔說(shuō)明使用的結(jié)尾三引號(hào)應(yīng)該自成一行,例如:
"""Return a foobang

Optional plotz says to frobnicate the bizbaz first.
"""
  • 對(duì)于單行的文檔說(shuō)明,尾部的三引號(hào)應(yīng)該和文檔在同一行。

命名規(guī)范

Python庫(kù)的命名規(guī)范很亂,從來(lái)沒(méi)能做到完全一致。但是目前有一些推薦的命名標(biāo)準(zhǔn)。新的模塊和包(包括第三方框架)應(yīng)該用這套標(biāo)準(zhǔn),但當(dāng)一個(gè)已有庫(kù)采用了不同的風(fēng)格,推薦保持內(nèi)部一致性。

最重要的原則

那些暴露給用戶(hù)的API接口的命名,應(yīng)該遵循反映使用場(chǎng)景而不是實(shí)現(xiàn)的原則。

描述:命名風(fēng)格

有許多不同的命名風(fēng)格。這里能夠幫助大家識(shí)別正在使用什么樣的命名風(fēng)格,而不考慮他們?yōu)槭裁词褂谩?br> 以下是常見(jiàn)的命名方式:

  • b(單個(gè)小寫(xiě)字母)
  • B(單個(gè)大寫(xiě)字母)
  • lowercase 小寫(xiě)字母
  • lower_case_with_underscores 使用下劃線分隔的小寫(xiě)字母
  • UPPERCASE 大寫(xiě)字母
  • UPPER_CASE_WITH_UNDERSCORES 使用下劃線分隔的大寫(xiě)字母
  • CapitalizedWords(或者叫 CapWords,或者叫CamelCase 駝峰命名法 —— 這么命名是因?yàn)樽帜缚瓷先ビ衅鸱耐庥^5)。有時(shí)候也被稱(chēng)為StudlyCaps。
    注意:當(dāng)在首字母大寫(xiě)的風(fēng)格中用到縮寫(xiě)時(shí),所有縮寫(xiě)的字母用大寫(xiě),因此,HTTPServerError 比 HttpServerError 好。
  • mixedCase(不同于首字母大寫(xiě),第一個(gè)單詞的首字母小寫(xiě))
  • Capitalized_Words_With_Underscores(巨丑無(wú)比!)

也有用唯一的短前綴把相關(guān)命名組織在一起的方法。這在Python中不常用,但還是提一下。比如,os.stat()函數(shù)中包含類(lèi)似以st_mode,st_size,st_mtime這種傳統(tǒng)命名方式命名的變量。(這么做是為了與 POSIX 系統(tǒng)的調(diào)用一致,以幫助程序員熟悉它。)
X11庫(kù)的所有公共函數(shù)都加了前綴X。在Python里面沒(méi)必要這么做,因?yàn)閷傩院头椒ㄔ谡{(diào)用的時(shí)候都會(huì)用類(lèi)名做前綴,函數(shù)名用模塊名做前綴。
另外,下面這種用前綴或結(jié)尾下劃線的特殊格式是被認(rèn)可的(通常和一些約定相結(jié)合):

  • _single_leading_underscore:(單下劃線開(kāi)頭)弱“內(nèi)部使用”指示器。比如 from M import * 是不會(huì)導(dǎo)入以下劃線開(kāi)始的對(duì)象的。
  • single_trailing_underscore_:(單下劃線結(jié)尾)這是避免和Python內(nèi)部關(guān)鍵詞沖突的一種約定,比如:Tkinter.Toplevel(master, class_=’ClassName’)
  • __double_leading_underscore:(雙下劃線開(kāi)頭)當(dāng)這樣命名一個(gè)類(lèi)的屬性時(shí),調(diào)用它的時(shí)候名字會(huì)做矯正(在類(lèi)FooBar中,__boo變成了_FooBar__boo;見(jiàn)下文)。
  • __double_leading_and_trailing_underscore__:(雙下劃線開(kāi)頭,雙下劃線結(jié)尾)“magic”對(duì)象或者存在于用戶(hù)控制的命名空間內(nèi)的屬性,例如:init,import或者file。除了作為文檔之外,永遠(yuǎn)不要命這樣的名。

命名約定

應(yīng)避免的名字

永遠(yuǎn)不要使用字母‘l’(小寫(xiě)的L),‘O’(大寫(xiě)的O),或者‘I’(大寫(xiě)的I)作為單字符變量名。
在有些字體里,這些字符無(wú)法和數(shù)字0和1區(qū)分,如果想用‘l’,用‘L’代替。

包名和模塊名

模塊應(yīng)該用簡(jiǎn)短全小寫(xiě)的名字,如果為了提升可讀性,下劃線也是可以用的。Python包名也應(yīng)該使用簡(jiǎn)短全小寫(xiě)的名字,但不建議用下劃線。
當(dāng)使用C或者C++編寫(xiě)了一個(gè)依賴(lài)于提供高級(jí)(更面向?qū)ο螅┙涌诘腜ython模塊的擴(kuò)展模塊,這個(gè)C/C++模塊需要一個(gè)下劃線前綴(例如:_socket)

類(lèi)名

類(lèi)名一般使用首字母大寫(xiě)的約定。
在接口被文檔化并且主要被用于調(diào)用的情況下,可以使用函數(shù)的命名風(fēng)格代替。
注意,對(duì)于內(nèi)置的變量命名有一個(gè)單獨(dú)的約定:大部分內(nèi)置變量是單個(gè)單詞(或者兩個(gè)單詞連接在一起),首字母大寫(xiě)的命名法只用于異常名或者內(nèi)部的常量。

異常名

因?yàn)楫惓R话愣际穷?lèi),所有類(lèi)的命名方法在這里也適用。然而,你需要在異常名后面加上“Error”后綴(如果異常確實(shí)是一個(gè)錯(cuò)誤)。

全局變量名

(我們希望這一類(lèi)變量只在模塊內(nèi)部使用。)約定和函數(shù)命名規(guī)則一樣。
通過(guò) from M import * 導(dǎo)入的模塊應(yīng)該使用all機(jī)制去防止內(nèi)部的接口對(duì)外暴露,或者使用在全局變量前加下劃線的方式(表明這些全局變量是模塊內(nèi)非公有)。

函數(shù)名

函數(shù)名應(yīng)該小寫(xiě),如果想提高可讀性可以用下劃線分隔。
大小寫(xiě)混合僅在為了兼容原來(lái)主要以大小寫(xiě)混合風(fēng)格的情況下使用(比如 threading.py),保持向后兼容性。

函數(shù)和方法參數(shù)

始終要將 self 作為實(shí)例方法的的第一個(gè)參數(shù)。
始終要將 cls 作為類(lèi)靜態(tài)方法的第一個(gè)參數(shù)。
如果函數(shù)的參數(shù)名和已有的關(guān)鍵詞沖突,在最后加單一下劃線比縮寫(xiě)或隨意拼寫(xiě)更好。因此 class_ 比 clss 更好。(也許最好用同義詞來(lái)避免這種沖突)

方法名和實(shí)例變量

遵循這樣的函數(shù)命名規(guī)則:使用下劃線分隔小寫(xiě)單詞以提高可讀性。
在非共有方法和實(shí)例變量前使用單下劃線。
通過(guò)雙下劃線前綴觸發(fā)Python的命名轉(zhuǎn)換規(guī)則來(lái)避免和子類(lèi)的命名沖突。
Python通過(guò)類(lèi)名對(duì)這些命名進(jìn)行轉(zhuǎn)換:如果類(lèi) Foo 有一個(gè)叫__a 的成員變量, 它無(wú)法通過(guò)Foo.__a 訪問(wèn)。(執(zhí)著的用戶(hù)可以通過(guò) Foo._Foo__a 訪問(wèn)。)一般來(lái)說(shuō),前綴雙下劃線用來(lái)避免類(lèi)中的屬性命名與子類(lèi)沖突的情況。
注意:關(guān)于__names的用法存在爭(zhēng)論(見(jiàn)下文)。

常量

常量通常定義在模塊級(jí),通過(guò)下劃線分隔的全大寫(xiě)字母命名。例如: MAX_OVERFLOW 和 TOTAL。

繼承的設(shè)計(jì)

始終要考慮到一個(gè)類(lèi)的方法和實(shí)例變量(統(tǒng)稱(chēng):屬性)應(yīng)該是共有還是非共有。如果存在疑問(wèn),那就選非共有;因?yàn)閷⒁粋€(gè)非共有變量轉(zhuǎn)為共有比反過(guò)來(lái)更容易。
公共屬性是那些與類(lèi)無(wú)關(guān)的客戶(hù)使用的屬性,并承諾避免向后不兼容的更改。非共有屬性是那些不打算讓第三方使用的屬性;你不需要承諾非共有屬性不會(huì)被修改或被刪除。
我們不使用“私有(private)”這個(gè)說(shuō)法,是因?yàn)樵赑ython中目前還沒(méi)有真正的私有屬性(為了避免大量不必要的常規(guī)工作)。
另一種屬性作為子類(lèi)API的一部分(在其他語(yǔ)言中通常被稱(chēng)為“protected”)。有些類(lèi)是專(zhuān)為繼承設(shè)計(jì)的,用來(lái)擴(kuò)展或者修改類(lèi)的一部分行為。當(dāng)設(shè)計(jì)這樣的類(lèi)時(shí),要謹(jǐn)慎決定哪些屬性時(shí)公開(kāi)的,哪些是作為子類(lèi)的API,哪些只能在基類(lèi)中使用。
貫徹這樣的思想,一下是一些讓代碼Pythonic的準(zhǔn)則:

  • 公共屬性不應(yīng)該有前綴下劃線。
  • 如果公共屬性名和關(guān)鍵字沖突,在屬性名之后增加一個(gè)下劃線。這比縮寫(xiě)和隨意拼寫(xiě)好很多。(然而,盡管有這樣的規(guī)則,在作為參數(shù)或者變量時(shí),‘cls’是表示‘類(lèi)’最好的選擇,特別是作為類(lèi)方法的第一個(gè)參數(shù)。)
    注意1:參考之前的類(lèi)方法參數(shù)命名建議
  • 對(duì)于單一的共有屬性數(shù)據(jù),最好直接對(duì)外暴露它的變量名,而不是通過(guò)負(fù)責(zé)的 存取器(accessor)/突變(mutator) 方法。請(qǐng)記住,如果你發(fā)現(xiàn)一個(gè)簡(jiǎn)單的屬性需要成長(zhǎng)為一個(gè)功能行為,那么Python為這種將來(lái)會(huì)出現(xiàn)的擴(kuò)展提供了一個(gè)簡(jiǎn)單的途徑。在這種情況下,使用屬性去隱藏屬性數(shù)據(jù)訪問(wèn)背后的邏輯。
    注意1:屬性只在new-style類(lèi)中起作用。
    注意2:盡管功能方法對(duì)于類(lèi)似緩存的負(fù)面影響比較小,但還是要盡量避免。
    注意3:屬性標(biāo)記會(huì)讓調(diào)用者認(rèn)為開(kāi)銷(xiāo)(相當(dāng)?shù)模┬。苊庥脤傩宰鲩_(kāi)銷(xiāo)大的計(jì)算。
  • 如果你的類(lèi)打算用來(lái)繼承的話,并且這個(gè)類(lèi)里有不希望子類(lèi)使用的屬性,就要考慮使用雙下劃線前綴并且沒(méi)有后綴下劃線的命名方式。這會(huì)調(diào)用Python的命名轉(zhuǎn)換算法,將類(lèi)的名字加入到屬性名里。這樣做可以幫助避免在子類(lèi)中不小心包含了相同的屬性名而產(chǎn)生的沖突。
    注意1:只有類(lèi)名才會(huì)整合進(jìn)屬性名,如果子類(lèi)的屬性名和類(lèi)名和父類(lèi)都相同,那么你還是會(huì)有命名沖突的問(wèn)題。
    注意2:命名轉(zhuǎn)換會(huì)在某些場(chǎng)景使用起來(lái)不太方便,例如調(diào)試,getattr()。然而命名轉(zhuǎn)換的算法有很好的文檔說(shuō)明并且很好操作。
    注意3:不是所有人都喜歡命名轉(zhuǎn)換。盡量避免意外的名字沖突和潛在的高級(jí)調(diào)用。

公共和內(nèi)部的接口

任何向后兼容保證只適用于公共接口,因此,用戶(hù)清晰地區(qū)分公共接口和內(nèi)部接口非常重要。
文檔化的接口被認(rèn)為是公開(kāi)的,除非文檔明確聲明它們是臨時(shí)或內(nèi)部接口,不受通常的向后兼容性保證。所有未記錄的接口都應(yīng)該是內(nèi)部的。
為了更好地支持內(nèi)?。╥ntrospection),模塊應(yīng)該使用__all__屬性顯式地在它們的公共API中聲明名稱(chēng)。將__all__設(shè)置為空列表表示模塊沒(méi)有公共API。
即使通過(guò)__all__設(shè)置過(guò),內(nèi)部接口(包,模塊,類(lèi),方法,屬性或其他名字)依然需要單個(gè)下劃線前綴。
如果一個(gè)命名空間(包,模塊,類(lèi))被認(rèn)為是內(nèi)部的,那么包含它的接口也應(yīng)該被認(rèn)為是內(nèi)部的。
導(dǎo)入的名稱(chēng)應(yīng)該始終被視作是一個(gè)實(shí)現(xiàn)的細(xì)節(jié)。其他模塊必須不能間接訪問(wèn)這樣的名稱(chēng),除非它是包含它的模塊中有明確的文檔說(shuō)明的API,例如 os.path 或者是一個(gè)包里從子模塊公開(kāi)函數(shù)接口的__init__ 模塊。

編程建議

  • 代碼應(yīng)該用不損害其他Python實(shí)現(xiàn)的方式去編寫(xiě)(PyPy,Jython,IronPython,Cython,Psyco 等)。
    比如,不要依賴(lài)于在CPython中高效的內(nèi)置字符連接語(yǔ)句 a += b 或者 a = a + b。這種優(yōu)化甚至在CPython中都是脆弱的(它只適用于某些類(lèi)型)并且沒(méi)有出現(xiàn)在不使用引用計(jì)數(shù)的實(shí)現(xiàn)中。在性能要求比較高的庫(kù)中,可以種 ”.join() 代替。這可以確保字符關(guān)聯(lián)在不同的實(shí)現(xiàn)中都可以以線性時(shí)間發(fā)生。
  • 像None這樣的單例對(duì)象進(jìn)行比較的時(shí)候應(yīng)該始終用 is 或者 is not,永遠(yuǎn)不要用等號(hào)運(yùn)算符。
    另外,如果你在寫(xiě) if x 的時(shí)候,請(qǐng)注意你是否表達(dá)的意思是 if x is not None。舉個(gè)例子,當(dāng)測(cè)試一個(gè)默認(rèn)值為None的變量或者參數(shù)是否被設(shè)置為其他值的時(shí)候。這個(gè)其他值應(yīng)該是在上下文中能成為bool類(lèi)型false的值。
  • 使用 is not 運(yùn)算符,而不是 not … is 。雖然這兩種表達(dá)式在功能上完全相同,但前者更易于閱讀,所以?xún)?yōu)先考慮。
    推薦:
if foo is not None:

不推薦:

if not foo is None:
  • 當(dāng)使用富比較(rich comparisons,一種復(fù)雜的對(duì)象間比較的新機(jī)制,允許返回值不為-1,0,1)實(shí)現(xiàn)排序操作的時(shí)候,最好實(shí)現(xiàn)全部的六個(gè)操作符(__eq__, __ne__, __lt__, __gt__, __ge__)而不是依靠其他的代碼去實(shí)現(xiàn)特定的比較。
    為了最大程度減少這一過(guò)程的開(kāi)銷(xiāo), functools.total_ordering() 修飾符提供了用于生成缺少的比較方法的工具。
    PEP 207 指出Python實(shí)現(xiàn)了反射機(jī)制。因此,解析器會(huì)將 y > x 轉(zhuǎn)變?yōu)?x < y,將 y >= x 轉(zhuǎn)變?yōu)?x <= y,也會(huì)轉(zhuǎn)換x == y 和 x != y的參數(shù)。sort() 和 min()方法確保使用<操作符,max()使用>操作符。然而,最好還是實(shí)現(xiàn)全部六個(gè)操作符,以免在其他地方出現(xiàn)沖突。
  • 始終使用def表達(dá)式,而不是通過(guò)賦值語(yǔ)句將lambda表達(dá)式綁定到一個(gè)變量上。
    推薦:
def f(x): return 2*x

不推薦:

f = lambda x: 2*x

第一個(gè)形式意味著生成的函數(shù)對(duì)象的名稱(chēng)是“f”而不是泛型“< lambda >”。這在回溯和字符串顯示的時(shí)候更有用。賦值語(yǔ)句的使用消除了lambda表達(dá)式優(yōu)于顯式def表達(dá)式的唯一優(yōu)勢(shì)(即lambda表達(dá)式可以?xún)?nèi)嵌到更大的表達(dá)式中)。

  • 從Exception繼承異常,而不是BaseException。直接繼承BaseException的異常適用于幾乎不用來(lái)捕捉的異常。
    設(shè)計(jì)異常的等級(jí),要基于撲捉異常代碼的需要,而不是異常拋出的位置。以編程的方式去回答“出了什么問(wèn)題?”,而不是只是確認(rèn)“出現(xiàn)了問(wèn)題”(內(nèi)置異常結(jié)構(gòu)的例子參考 PEP 3151
    類(lèi)的命名規(guī)范適用于這里,但是你需要添加一個(gè)“Error”的后綴到你的異常類(lèi),如果異常是一個(gè)Error的話。非本地流控制或者其他形式的信號(hào)的非錯(cuò)誤異常不需要特殊的后綴。

  • 適當(dāng)?shù)厥褂卯惓f溄?。在Python 3里,為了不丟失原始的根源,可以顯式指定“raise X from Y”作為替代。
    當(dāng)故意替換一個(gè)內(nèi)部異常時(shí)(Python 2 使用“raise X”, Python 3.3 之后 使用 “raise X from None”),確保相關(guān)的細(xì)節(jié)轉(zhuǎn)移到新的異常中(比如把AttributeError轉(zhuǎn)為KeyError的時(shí)候保留屬性名,或者將原始異常信息的文本內(nèi)容內(nèi)嵌到新的異常中)。

  • 在Python 2中拋出異常時(shí),使用 rasie ValueError(‘message’) 而不是用老的形式 raise ValueError, ‘message’。
    第二種形式在Python3 的語(yǔ)法中不合法
    使用小括號(hào),意味著當(dāng)異常里的參數(shù)非常長(zhǎng),或者包含字符串格式化的時(shí)候,不需要使用換行符。

  • 當(dāng)捕獲到異常時(shí),如果可以的話寫(xiě)上具體的異常名,而不是只用一個(gè)except: 塊。
    比如說(shuō):

try:
    import platform_specific_module
except ImportError:
    platform_specific_module = None

如果只有一個(gè)except: 塊將會(huì)捕獲到SystemExit和KeyboardInterrupt異常,這樣會(huì)很難通過(guò)Control-C中斷程序,而且會(huì)掩蓋掉其他問(wèn)題。如果你想捕獲所有指示程序出錯(cuò)的異常,使用 except Exception: (只有except等價(jià)于 except BaseException:)。
兩種情況不應(yīng)該只使用‘excpet’塊:

  1. 如果異常處理的代碼會(huì)打印或者記錄log;至少讓用戶(hù)知道發(fā)生了一個(gè)錯(cuò)誤。
  2. 如果代碼需要做清理工作,使用 raise..try…finally 能很好處理這種情況并且能讓異常繼續(xù)上浮。

當(dāng)給捕捉的異常綁定一個(gè)名字時(shí),推薦使用在Python 2.6中加入的顯式命名綁定語(yǔ)法:

try:
    process_data()
except Exception as exc:
    raise DataProcessingFailedError(str(exc))

為了避免和原來(lái)基于逗號(hào)分隔的語(yǔ)法出現(xiàn)歧義,Python3只支持這一種語(yǔ)法。

  • 當(dāng)捕捉操作系統(tǒng)的錯(cuò)誤時(shí),推薦使用Python 3.3 中errno內(nèi)定數(shù)值指定的異常等級(jí)。

  • 另外,對(duì)于所有的 try/except 語(yǔ)句塊,在try語(yǔ)句中只填充必要的代碼,這樣能避免掩蓋掉bug。
    推薦:

try:
    value = collection[key]
except KeyError:
    return key_not_found(key)
else:
    return handle_value(value)

不推薦:

try:
    # Too broad!
    return handle_value(collection[key])
except KeyError:
    # Will also catch KeyError raised by handle_value()
    return key_not_found(key)
  • 當(dāng)代碼片段局部使用了某個(gè)資源的時(shí)候,使用with 表達(dá)式來(lái)確保這個(gè)資源使用完后被清理干凈。用try/finally也可以。
  • 無(wú)論何時(shí)獲取和釋放資源,都應(yīng)該通過(guò)單獨(dú)的函數(shù)或方法調(diào)用上下文管理器。舉個(gè)例子:
    推薦:
with conn.begin_transaction():
    do_stuff_in_transaction(conn)

不推薦:

with conn:
    do_stuff_in_transaction(conn)

第二個(gè)例子沒(méi)有提供任何信息去指明__enter____exit__方法在事務(wù)之后做出了關(guān)閉連接之外的其他事情。這種情況下,明確指明非常重要。

  • 返回的語(yǔ)句保持一致。函數(shù)中的返回語(yǔ)句都應(yīng)該返回一個(gè)表達(dá)式,或者都不返回。如果一個(gè)返回語(yǔ)句需要返回一個(gè)表達(dá)式,那么在沒(méi)有值可以返回的情況下,需要用 return None 顯式指明,并且在函數(shù)的最后顯式指定一條返回語(yǔ)句(如果能跑到那的話)。
    推薦:
def foo(x):
    if x >= 0:
        return math.sqrt(x)
    else:
        return None

def bar(x):
    if x < 0:
        return None
    return math.sqrt(x)

不推薦:

def foo(x):
    if x >= 0:
        return math.sqrt(x)

def bar(x):
    if x < 0:
        return
    return math.sqrt(x)
  • 使用字符串方法代替字符串模塊。
    字符串方法總是更快,并且和unicode字符串分享相同的API。如果需要兼容Python2.0之前的版本可以不用考慮這個(gè)規(guī)則。
  • 使用 ”.startswith() 和 ”.endswith() 代替通過(guò)字符串切割的方法去檢查前綴和后綴。
    startswith()和endswith()更干凈,出錯(cuò)幾率更小。比如:
推薦: if foo.startswith('bar'):
糟糕: if foo[:3] == 'bar':
  • 對(duì)象類(lèi)型的比較應(yīng)該用isinstance()而不是直接比較type。
正確: if isinstance(obj, int):

糟糕: if type(obj) is type(1):

當(dāng)檢查一個(gè)對(duì)象是否為string類(lèi)型時(shí),記住,它也有可能是unicode string!在Python2中,str和unicode都有相同的基類(lèi):basestring,所以你可以這樣:

if isinstance(obj, basestring):

注意,在Python3中,unicode和basestring都不存在了(只有str)并且bytes類(lèi)型的對(duì)象不再是string類(lèi)型的一種(它是整數(shù)序列)

  • 對(duì)于序列來(lái)說(shuō)(strings,lists,tuples),可以使用空序列為false的情況。
正確: if not seq:
      if seq:

糟糕: if len(seq):
      if not len(seq):
  • 書(shū)寫(xiě)字符串時(shí)不要依賴(lài)單詞結(jié)尾的空格,這樣的空格在視覺(jué)上難以區(qū)分,有些編輯器會(huì)自動(dòng)去掉他們(比如 reindent.py (譯注:re indent 重新縮進(jìn)))
  • 不要用 == 去和True或者False比較:
正確: if greeting:
糟糕: if greeting == True:
更糟: if greeting is True:

功能注釋

隨著PEP 484的引入,功能型注釋的風(fēng)格規(guī)范有些變化。

  • 為了向前兼容,在Python3代碼中的功能注釋?xiě)?yīng)該使用 PEP 484的語(yǔ)法規(guī)則。(在前面的章節(jié)中對(duì)注釋有格式化的建議。)
  • 不再鼓勵(lì)使用之前在PEP中推薦的實(shí)驗(yàn)性樣式。
  • 然而,在stdlib庫(kù)之外,在PEP 484中的實(shí)驗(yàn)性規(guī)則是被鼓勵(lì)的。比如用PEP 484的樣式標(biāo)記大型的第三方庫(kù)或者應(yīng)用程序,回顧添加這些注釋是否簡(jiǎn)單,并觀察是否增加了代碼的可讀性。
  • Python的標(biāo)準(zhǔn)庫(kù)代碼應(yīng)該保守使用這種注釋?zhuān)碌拇a或者大型的重構(gòu)可以使用這種注釋。
  • 如果代碼希望對(duì)功能注釋有不同的用途,建議在文件的頂部增加一個(gè)這種形式的注釋?zhuān)?/li>
# type: ignore

這會(huì)告訴檢查器忽略所有的注釋。(在 PEP 484中可以找到從類(lèi)型檢查器禁用投訴的更細(xì)粒度的方法。)

  • 像linters一樣,類(lèi)型檢測(cè)器是可選的可獨(dú)立的工具。默認(rèn)情況下,Python解釋器不應(yīng)該因?yàn)轭?lèi)型檢查而發(fā)出任何消息,也不應(yīng)該基于注釋改變它們的行為。

  • 不想使用類(lèi)型檢測(cè)的用戶(hù)可以忽略他們。然而,第三方庫(kù)的用戶(hù)可能希望在這些庫(kù)上運(yùn)行類(lèi)型檢測(cè)。為此, PEP 484 建議使用存根文件類(lèi)型:.pyi文件,這種文件類(lèi)型相比于.py文件會(huì)被類(lèi)型檢測(cè)器讀取。存根文件可以和庫(kù)一起,或者通過(guò)typeshed repo6獨(dú)立發(fā)布(通過(guò)庫(kù)作者的許可)

  • 對(duì)于需要向后兼容的代碼,可以以注釋的形式添加功能型注釋。參見(jiàn)PEP 484的相關(guān)部分7。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容