列出乘法和公共參考

考慮通過乘以建立巢狀列表結構的情況:

li = [[]] * 3
print(li)
# Out: [[], [], []]

乍一看,我們認為我們有一個包含 3 個不同巢狀列表的列表。讓我們嘗試將 1 新增到第一個:

li[0].append(1)
print(li)
# Out: [[1], [1], [1]]

1 被附加到 li 中的所有列表中。

原因是 [[]] * 3 沒有創造出 3 種不同的 lists。相反,它建立了一個 list,包含對同一 list 物件的 3 個引用。因此,當我們附加到 li[0] 時,變化在 li 的所有子元素中都可見。這相當於:

li = []
element = [[]]
li = element + element + element
print(li)
# Out: [[], [], []]
element.append(1)
print(li)
# Out: [[1], [1], [1]]

如果我們使用 id 列印包含的 list 的記憶體地址,這可以進一步證實:

li = [[]] * 3
print([id(inner_list) for inner_list in li])
# Out: [6830760, 6830760, 6830760]

解決方案是使用迴圈建立內部列表:

li = [[] for _ in range(3)]

我們現在建立 3 個不同的不同列表,而不是建立單個 list 然後對其進行 3 次引用。這也可以通過使用 id 函式進行驗證:

print([id(inner_list) for inner_list in li])
# Out: [6331048, 6331528, 6331488]

你也可以這樣做。它會導致在每個 append 呼叫中建立一個新的空列表。

>>> li = []
>>> li.append([])
>>> li.append([])
>>> li.append([])
>>> for k in li: print(id(k))
... 
4315469256
4315564552
4315564808

不要使用索引迴圈序列。

別:

for i in range(len(tab)):
    print(tab[i])

for elem in tab:
    print(elem)

for 將為你自動執行大多數迭代操作。

如果你確實需要索引和元素,請使用列舉

for i, elem in enumerate(tab):
     print((i, elem))

使用“==”檢查 True 或 False 時要小心

if (var == True):
    # this will execute if var is True or 1, 1.0, 1L

if (var != True):
    # this will execute if var is neither True nor 1

if (var == False):
    # this will execute if var is False or 0 (or 0.0, 0L, 0j)

if (var == None):
    # only execute if var is None

if var:
    # execute if var is a non-empty string/list/dictionary/tuple, non-0, etc

if not var:
    # execute if var is "", {}, [], (), 0, None, etc.

if var is True:
    # only execute if var is boolean True, not 1

if var is False:
    # only execute if var is boolean False, not 0

if var is None:
    # same as var == None

不要檢查是否可以,只是這樣做並處理錯誤

Pythonistas 通常會說請求寬恕比允許更容易

別:

if os.path.isfile(file_path):
    file = open(file_path)
else:
    # do something

做:

try:
    file = open(file_path)
except OSError as e:
    # do something

或者甚至更好用 Python 2.6+

with open(file_path) as file:

它更好,因為它更通用。你可以將 try/except 應用於幾乎任何東西。你無需關心如何防止它,只關心你所面臨的錯誤。

不要檢查型別

Python 是動態型別的,因此檢查型別會使你失去靈活性。相反,通過檢查行為使用 duck typing 。如果你希望函式中包含字串,請使用 str() 將任何物件轉換為字串。如果你需要列表,請使用 list() 將任何可迭代轉換為列表。

別:

def foo(name):
    if isinstance(name, str):
        print(name.lower())

def bar(listing):
    if isinstance(listing, list):
        listing.extend((1, 2, 3))
        return ", ".join(listing)

做:

def foo(name) :
    print(str(name).lower())

def bar(listing) :
    l = list(listing)
    l.extend((1, 2, 3))
    return ", ".join(l)

使用最後一種方式,foo 將接受任何物件。bar 將接受字串,元組,集合,列表等等。便宜幹。

不要混合空格和製表符

使用 object 作為第一個父物件

這很棘手,但隨著程式的增長,它會咬你。在 Python 2.x 有新舊類。舊的,舊的。它們缺少某些功能,並且可能具有繼承的笨拙行為。為了可以使用,你的任何一個類必須是新風格。為此,請將其從 object 繼承。

別:

class Father:
    pass

class Child(Father):
    pass

做:

class Father(object):
    pass

class Child(Father):
    pass

Python 3.x,所有類都是新風格,所以你不需要這樣做。

不要在 init 方法之外初始化類屬性

來自其他語言的人發現它很誘人,因為這是你用 Java 或 PHP 做的。編寫類名,然後列出屬性併為其指定預設值。它似乎在 Python 中工作,然而,這並不像你想象的那樣工作。這樣做會設定類屬性(靜態屬性),然後當你嘗試獲取物件屬性時,它將為你提供它的值,除非它是空的。在這種情況下,它將返回類屬性。這意味著兩大危害:

  • 如果更改了 class 屬性,則更改初始值。

  • 如果將可變物件設定為預設值,則將獲得跨例項共享的相同物件。

不要(除非你想要靜態):

class Car(object):
    color = "red"
    wheels = [Wheel(), Wheel(), Wheel(), Wheel()]

做:

class Car(object):
    def __init__(self):
        self.color = "red"
        self.wheels = [Wheel(), Wheel(), Wheel(), Wheel()]