Python列表乘法和公共引用

示例

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

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

乍一看,我们认为我们有一个包含3个不同嵌套列表的列表。让我们尝试附加1到第一个:

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

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

原因是[[]] * 3不会创建list3个不同list的。而是,它创建了一个list持有3个对同一list对象的引用。因此,当我们追加li[0]更改时,在的所有子元素中都可见li。这等效于:

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

这可以进一步证实,如果我们打印的内容包含在存储地址list使用id:

li = [[]] * 3
print([id(inner_list) for inner_list in li])
# 输出:[6830760、6830760、6830760]

解决方案是使用循环创建内部列表:

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

list现在,我们创建3个不同的列表,而不是先创建一个,然后再对其进行3个引用。同样,可以使用以下id函数来验证:

print([id(inner_list) for inner_list in li])
# 输出:[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):
    # 如果var为True或1、1.0、1L,则将执行此操作

if (var != True):
    # 如果var既不是True也不是1,则将执行此操作

if (var == False):
    # 如果var为False或0(或0.0、0L,0j),则将执行此操作

if (var == None):
    # 仅在var为None时执行

if var:
    # 如果var是非空字符串/列表/字典/元组,非0等,则执行

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

if var is True:
    # 仅在var为布尔值True而不是1时执行

if var is False:
    # 仅在var为布尔值False而不是0时执行

if var is None:
    # 与var ==无相同

不要检查是否可以,只需执行并处理错误

Pythonista通常会说:“请求宽恕比允许许可容易。”

不要:

if os.path.isfile(file_path):
    file = open(file_path)
else:
    # 做点什么

应该这样:

try:
    file = open(file_path)
except OSError as e:
    # 做点什么

甚至更好Python 2.6+:

with open(file_path) as file:

它更好,因为它更通用。您try/except几乎可以申请任何东西。您无需关心如何采取预防措施,只需关心您所冒的错误。

不检查类型

Python是动态类型的,因此检查类型会使您失去灵活性。而是通过检查行为来使用鸭子类型。如果您希望函数中有字符串,请使用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将接受字符串,元组,集合,列表等等。便宜的干。

不要混用空格和制表符

对象用作第一父对象

这很棘手,但是随着程序的增长它会咬你。中有新旧类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()]