Python:在字符串变化时循环遍历字符
def substitutionEncrypt1(text,key):
plaintext=text.lower()
alphabet="abcdefghijklmnopqrstuvwxyz "
for ch in plaintext:
r=plaintext.index(ch)
plaintext=plaintext.replace(ch,key[r])
return plaintext
我的文本是 "The Quick Brown Fox"。 我明白为什么它没有正确加密替换密码,但为什么在第五行时,Python会报错说“没有找到子字符串”? 我不是在遍历明文中的字符吗? 我使用的语言是Python。
谢谢!
2 个回答
你的代码有几个问题,导致它根本无法达到你想要的效果。
1. 字符串在Python中是不可变的
在Python中,你不能改变一个字符串。一旦创建了字符串,它就保持不变。你能做的只是基于原来的字符串创建一个新的字符串,并把它赋值给同一个名字。
比如,你可以通过切片的方式来修改一个list
(可变类型),在某个位置添加一个新元素:
>>> lst = ['one', 'two', 'three']
>>> lst[0] = 42
>>> lst
[42, 'two', 'three']
但是你不能这样做字符串,因为它们是不可变的:
>>> s = 'abc'
>>> s[1] = 42
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
所以,你能做的就是给s
赋值一个全新的字符串:
>>> s = s.upper()
>>> s
'ABC'
因此,像upper()
、replace()
以及其他字符串方法,都不会直接在原字符串上修改,而是返回一个修改后的新字符串。
至于为什么字符串是不可变的,可以参考这个链接:为什么Python字符串是不可变的?。简单来说,这主要是为了提高性能。
2. 你的循环并没有遍历s
,而是遍历了循环开始时s
的值
当你写for item in iterable
时,Python会这样做:
- 它获取一个指向循环开始时
iterable
指向的值的引用。- 如果这个值是字符串,它会获取对那个确切字符串的引用。由于字符串是不可变的,这个引用始终指向内存中的同一个对象。
- 如果这个值是可变的(比如列表),引用会指向那个值。
- 每次循环时,它会从引用指向的可迭代对象中获取下一个项目。如果这个值是可变的,并且在此期间发生了变化,那就糟糕了,你会得到奇怪的结果。
3. 在遍历可变序列时不应该修改它们
即使str
是可变类型,并且你可以修改它,你也不应该在遍历时修改可迭代对象。
举个例子:
lst = [1, 2, 3, 4, 5]
for item in lst:
print item,
lst.remove(item)
输出:
1 3 5
如你所见,由于在遍历过程中列表的大小和项目的索引发生了变化,某些项目被跳过了。
对于字典,Python甚至会明确警告你,并中断执行:
dct = dict(foo=23, bar=42)
for key in dct:
print key
dct['baz'] = 'qux'
输出:
foo
Traceback (most recent call last):
File "it.py", line 3, in <module>
for key in dct:
RuntimeError: dictionary changed size during iteration
所以,考虑到这些,如果你想按照你原本的意图来实现代码,你需要将两个变量分开,一个用于遍历,另一个用于存放转换后的结果。
但因为Python已经有一个translate()
函数(正如@Óscar López的回答中解释的那样)专门用于这个目的,所以你应该使用这个函数。
你不应该在遍历某个东西的时候(无论是列表、集合还是字典……你懂的)同时对它进行修改。此外,字符串是不可变的,这意味着在每次遍历时,你实际上是在创建新的字符串。要在Python中实现替换密码,没有比使用 translate()
更简单的了——而且我们可以让翻译在两个方向上都能工作:
import string
alphabet = 'abcdefghijklmnopqrstuvwxyz '
subst = 'edbjvushlpknyxt foqiwacrzgm'
table = string.maketrans(alphabet, subst)
'hello world'.translate(table)
=> 'hvnntmctonj'
table = string.maketrans(subst, alphabet)
'hvnntmctonj'.translate(table)
=> 'hello world'