为什么我需要创建一个空字符串来替换字符串中的所有字符?

4
我正在编写一个ROT13函数,但是我不理解为什么以下代码不能正常工作:
def ROT(string):
    # y = 0
    for char in string:
        x = ord(char)
        if 97 <= x < 110 or 65 <= x < 78:
            # string[y]=char.replace(char, chr(x+13))
            char=char.replace(char, chr(x+13))
            print(char)
            # y+=1
        elif x >= 110 or 78 <= x < 91:
            # string[y]=char.replace(char, chr(x-13))
            char=char.replace(char, chr(x-13))
            print(char)
            # y+=1
    return string

string = ROT('Hello, lorem ipsum dolor sit amet')
print(string)

函数的调用只是打印原始字符串。如您在上面的注释行中所见(如果阅读起来有些困难,我深表歉意),我试图定义一个变量y来逐个遍历字符串,然后访问它,但我得到了一个运行时错误。 我想出的解决方案是在函数开头创建一个空字符串(从我的搜索结果来看,这似乎是大多数人使用的解决方案),但没有人解释为什么。 如果您要替换字符串中的每个字符,为什么不能返回原始字符串?


你具体遇到了什么错误? - thumbtackthief
4个回答

4
你代码中的问题在于你并没有操作原始字符串,只是替换了临时变量 char 而不是原始字符串。由于 Python 中的字符串是不可变的,因此你可以尝试使用一个新字符串,而不是替换原始字符串,并将字符附加到新字符串上。例如:
modified_string = ""
for char in string:
     #whatever condition
     modified_string += #value to be added

1
如果性能是一个问题,这个习语在Python中应该避免使用,因为它具有二次时间复杂度。 - mgilson
在循环中,modified_string += ... - mgilson
@mgilson 有其他更优的方法吗? - Aswin Murugesh
@AswinMurugesh -- 我在下面发布了另一个答案。你可以使用bytearray,它的行为方式更像是可变列表-字符串混合体。或者,正如我在我的答案中解释的那样,你可以使用string.translate - mgilson
@AswinMurugesh -- 是的,string.translate 不是最受欢迎的函数。但是这个想法并不难。你需要创建两个字符串--将字符串中的字符从 fromchr 翻译成 tochr 中相应的字符... - mgilson
显示剩余2条评论

2
您返回了原始字符串,请尝试:
def ROT(string):
#   y = 0
    result = ""
    for char in string:
        x = ord(char)
        if 97 <= x < 110 or 65 <= x < 78:
#           string[y]=char.replace(char, chr(x+13))
            char=char.replace(char, chr(x+13))
            result = result + char
            print(char)
            continue
#           y+=1
        elif x >= 110 or 78 <= x < 91:
#           string[y]=char.replace(char, chr(x-13))
            char=char.replace(char, chr(x-13))
            print(char)
            result = result + char
            continue
#           y+=1
        result = result + char
    return result

string = ROT('Hello, lorem ipsum dolor sit amet')
print(string)

2

Python中的字符串是不可变的。


这是我个人认为的问题的实际答案 :P - Joran Beasley

1

其他人已经解决了主要问题——字符串是不可变的,因此在迭代时无法切换单个字符。你可以使用bytearray,但是...

顺便提一下,这是string.translate的一个好的应用场景:

>>> import string
>>> fromchr = ''.join(chr(x) for x in range(97, 110) + range(65, 78))
>>> tochr = ''.join(chr(x+13) for x in range(97, 110) + range(65, 78))
>>> fromchr += ''.join(chr(x) for x in range(110, 256) + range(78, 91))
>>> tochr += ''.join(chr(x-13) for x in range(110, 256) + range(78, 91))
>>> trans = string.maketrans(fromchr, tochr)
>>> 'Hello, lorem ipsum dolor sit amet'.translate(trans)
'Uryyb, yberz vcfhz qbybe fvg nzrg'

这里的好处是创建翻译表只需要一次成本。创建完成后,您可以无限次使用它。在优化的C代码中,您的翻译将以~O(n)的时间发生,因此如果您能获得更快(或更简单)的实现,我会感到惊讶。
这种方式甚至比内置的'rot13'编解码器更方便。
def rot13a(s):
  return s.encode('rot13')

import string
fromchr = ''.join([chr(x) for x in range(97, 110) + range(65, 78)])
tochr = ''.join([chr(x+13) for x in range(97, 110) + range(65, 78)])
fromchr += ''.join(chr(x) for x in range(110, 256) + range(78, 91))
tochr += ''.join(chr(x-13) for x in range(110, 256) + range(78, 91))
trans = string.maketrans(fromchr, tochr)
def rot13b(s):
  return s.translate(trans)

import timeit
test_string = 'Hello, lorem ipsum dolor sit amet'
print rot13a(test_string) == rot13b(test_string)
print timeit.timeit("rot13a(test_string)", "from __main__ import test_string, rot13a")
print timeit.timeit("rot13b(test_string)", "from __main__ import test_string, rot13b")

我的结果:
True
1.52055001259  # rot13a
0.21444106102  # rot13b

请注意,这是Python 2.x的代码。在Python 3.x中,您不能像那样简单地添加范围,因为range不再返回list对象。但是,希望思路足够清晰...

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