两段Python代码的区别

2 投票
4 回答
591 浏览
提问于 2025-04-16 08:54

我正在做一个练习,内容如下:

# B. front_x
# Given a list of strings, return a list with the strings
# in sorted order, except group all the strings that begin with 'x' first.
# e.g. ['mix', 'xyz', 'apple', 'xanadu', 'aardvark'] yields
# ['xanadu', 'xyz', 'aardvark', 'apple', 'mix']
# Hint: this can be done by making 2 lists and sorting each of them
# before combining them.

示例解决方案:

def front_x(words):
  listX = []
  listO = []

  for w in words:
    if w.startswith('x'):
      listX.append(w)
    else:
      listO.append(w)

  listX.sort()
  listO.sort()

  return listX + listO

我的解决方案:

def front_x(words):
  listX = []

  for w in words:
    if w.startswith('x'):
      listX.append(w)
      words.remove(w)

  listX.sort()
  words.sort()

  return listX + words  

在我测试我的解决方案时,结果有点奇怪。这里是我解决方案的源代码:http://dl.dropbox.com/u/559353/list1.py。你可以试试看。

4 个回答

0

在遍历一个列表的时候,如果你同时对这个列表进行修改,会导致一些不可预知的结果。这就是为什么示例中会创建两个新列表,而不是直接从原列表中删除元素。

for w in words:
  if w.startswith('x'):
    listX.append(w)
    words.remove(w) # Problem here!

你可以查看 这个问题,里面讨论了这个问题。简单来说,列表的迭代器在遍历列表的索引时,并不会回头检查是否有修改(这样做会很耗费资源!)。

如果你不想创建第二个列表,那你就需要进行两次遍历。第一次遍历 words 来创建 listX,第二次遍历 listX 来从 words 中删除元素。

1

主要有两个不同之处:

  1. 在循环中从一个正在遍历的列表中删除元素,在Python里并不太好使。如果你用的是Java,系统会报错,告诉你不能在遍历一个集合的时候去修改它。而在Python中,似乎不会直接给你这个错误。@Felix_Kling在他的回答中解释得很清楚。
  2. 另外,你在修改输入参数words。这意味着调用你函数front_x的人会看到words在函数执行后被修改了。除非这是明确预期的行为,否则最好避免这种情况。想象一下,如果你的程序在别的地方也在使用words,那就会很麻烦。像sample solution中那样保持两个列表会是更好的做法。
3

问题在于你在遍历列表的时候,同时又在删除里面的元素(也就是在修改它):

for w in words:
    if w.startswith('x'):
      listX.append(w)
      words.remove(w)

举个例子:

>>> a = range(5)
>>> for i in a:
...  a.remove(i)
... 
>>> a
[1, 3]

这段代码的运行过程如下:

  • 先获取第一个元素,然后把它删除。
  • 接着移动到下一个元素。但因为我们刚才删除了0,所以现在1变成了新的第一个元素。这样下一个元素就变成了2,而1就被跳过了。
  • 同样的情况也发生在34上。

撰写回答