列出乘法和公共參考
考慮通過乘以建立巢狀列表結構的情況:
li = [[]] * 3
print(li)
# Out: [[], [], []]
乍一看,我們認為我們有一個包含 3 個不同巢狀列表的列表。讓我們嘗試將 1
新增到第一個:
li[0].append(1)
print(li)
# Out: [[1], [1], [1]]
1
被附加到 li
中的所有列表中。
原因是 [[]] * 3
沒有創造出 3 種不同的 list
s。相反,它建立了一個 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()]