列出乘法和公共参考
考虑通过乘以创建嵌套列表结构的情况:
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()]