列出乘法和公共参考

考虑通过乘以创建嵌套列表结构的情况:

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()]