用Python编写提取字符串中每个交替字母的程序?

10

Python程序通常很短小精悍,而其他编程语言中需要写一堆代码的东西(就我所知)在Python中可以用一两行代码完成。 我正在尝试编写一个这样的程序,从字符串中提取每隔一个字母的字符。 我有这个可行的代码,但想知道是否有其他简洁的方法?

>>> s
'abcdefg'
>>> b = ""
>>> for i in range(len(s)):
...   if (i%2)==0:
...      b+=s[i]
... 
>>> b
'aceg'
>>> 
6个回答

35
>>> 'abcdefg'[::2]
'aceg'

哇!那真的很简洁。虽然我知道切片是如何工作的,但我没有想到这个...有趣的是,所有其他答案都使用切片表示法发布。 - eagertoLearn
1
@eagertoLearn: "做一件事应该有一个——最好只有一个——明显的方法。" 这就是为什么所有发布的答案都做同样的事情的原因。 - abarnert
@abarnert:哈哈!我想这就是“Python之禅”吧,太美妙了。 - brain storm

18

解释一下Python的分片符号 (slice notation)

>>> 'abcdefg'[::2]
'aceg'
>>>

切片表示法的格式为 [start:stop:step]。因此,[::2] 告诉Python按照2的步幅来遍历字符串(这将返回每隔一个字符的字符)。


2

这里有一个关于列表和字符串的另一个例子:

sentence = "The quick brown fox jumped over the lazy dog."

sentence[::2]

这里的意思是:从字符串开头到结尾,返回每两个字符中的一个。

将返回以下内容:

'Teqikbonfxjme vrtelz o.'

您可以对列表执行相同操作:
colors = ["red", "organge", "yellow","green", "blue"]
colors[1:4]

would retrun:

['organge', 'yellow', 'green']

我阅读切片的方式是:如果我们有sentence[1:4],则从索引1(记住起始位置是索引0)开始,并在索引4之前停止。

2
这样做的正确方法是像其他答案中所述的那样对字符串进行切片。
但是,如果您想要一种更简洁的编写您的代码的方式,并且该方式适用于不像切片那样简单的类似问题,则有两个技巧:推导式enumerate函数。
首先,看下面的循环:
for i in range(len(foo)):
    value = foo[i]
    something with value and i

可以被写成:
for i, value in enumerate(foo):
    something with value and i

所以,在你的情况下:
for i, c in enumerate(s):
    if (i%2)==0:
        b+=c

接下来,任何以空对象开始、通过可迭代对象(字符串、列表、迭代器等)并将值放入新的可迭代对象的循环,可能会在值经过if筛选或表达式转换后,可以非常容易地转换为推导式。
虽然Python具有用于列表、集合、字典和迭代器的推导式,但它没有用于字符串的推导式,但是str.join解决了这个问题。
因此,把它们组合起来:
b = "".join(c for i, c in enumerate(s) if i%2 == 0)

远不如b = s[::2]那么简洁易读,但比你最初的做法好得多——当你想要做更复杂的事情时,例如if i%2 and i%3(这不能映射到任何明显的切片),或者用c*2将每个字母加倍(可以通过将两个切片一起压缩来完成,但这不是立即显然的),等等,同样的思路也适用。

2
如果我错了,请纠正我:你所拥有的不是列表推导式,而是生成器表达式,但这是列表推导式:b = "".join([c for i, c in enumerate(s) if i%2 == 0])。当然,在这里使用生成器最好。但只是想澄清一下。请不要误解我。 - brain storm
@user1988876:你说得完全正确。使用圆括号(或不使用任何符号)可以创建生成器表达式,方括号可以创建列表推导式,花括号可以创建集合或字典推导式。 - abarnert
@user1988876:此外,在传递给 str.join 的情况下,实际上没有什么区别——如果 str.join 得到的不是 C 中的快速可索引对象,则会将其转换为列表,以便可以快速索引。事实上(特别是在 3.4 之前),列表推导甚至可能更快且使用更少的内存,这一点与直觉相反。但从概念上讲,我认为在不需要构建列表时最好不要构建它。(而且,除非你知道 str.join 在底层是如何工作的,否则你不会知道你需要它。) - abarnert

0
你可以尝试使用切片和连接:
>>> k = list(s)
>>> "".join(k[::2])
'aceg'

或者直接按原样切割字符串。 - rlms
但是我还是给他点赞,使用 join 连接字符串比他现在的代码要好。 - rlms

0
实际上,切片是最好的方法。然而,你也可以通过使代码更具有Python风格来改进现有代码,而不是使其更短:
>>> s
'abcdefg'
>>> b = []
>>> for index, value in enumerate(s):
      if index % 2 == 0:
         b.append(value)
>>> b = "".join(b)

或者更好的是:

>>> b = "".join(value for index, value in enumerate(s) if index % 2 == 0)

这可以轻松地扩展到更复杂的条件:

>>> b = "".join(value for index, value in enumerate(s) if index % 2 == index % 3 == 0)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接