使用zip()的Python字符串字母替换

2024-09-28 21:48:38 发布

您现在位置:Python中文网/ 问答频道 /正文

我经历了一些Python的挑战,这个特别的挑战一直困扰着我的头脑,我认为它值得得到一些解释。内容如下:

Have the function LetterChanges(str) take the str parameter being passed and modify it using the following algorithm. Replace every letter in the string with the letter following it in the alphabet (ie. c becomes d, z becomes a). Then capitalize every vowel in this new string (a, e, i, o, u) and finally return this modified string.

例如:

Input: "fun times!"
Output: gvO Ujnft!

守则:

def LetterChanges(str):
  letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVW"
  changes = "bcdEfghIjklmnOpqrstUvwxyzABCDEFGHIJKLMNOPQRSTUVWZ"
  mapping = { k:v for (k,v) in zip(str+letters,str+changes) }
  return "".join([ mapping[c] for c in str ])

我知道它需要两个字符串,字母和变化。它使用zip()函数,该函数接受迭代器并将其“压缩”,形成字典形式的迭代器k:v for (k,v)这是一种口述理解

我的疑问是:

str+letters,str+changes到底发生了什么?为什么必须这样做

[ mapping[c] for c in str ]为什么这样做,我们就可以用它的值替换每个键,或者让它在质询描述中说:“用字母表中紧跟其后的字母替换字符串中的每个字母”


Tags: andtheinforstring字母itmapping
2条回答

What exactly is happening with str+letters,str+changes and why it had to be done?

因为输入字符串"fun times!"不仅仅包含字母表中的字母;它还包含一个空格' '和一个感叹号'!'。如果这些不是字典mapping中的键,那么mapping[c]将在c是这些字符之一时引发KeyError

因此zip(str + letters, str + changes)的目的是确保字符串中的每个字符都映射到字典中的自身,然后再将实际需要的转换添加到字典中。请注意,因为它是以str + ...开头的,所以str中的字母表中的任何字母都将首先映射到它们自己,然后letterschanges的映射将覆盖,然后

也就是说,使用mapping.get而不是mapping[...]会更简单,因为get方法允许在密钥不存在的情况下返回默认值。在这种情况下,我们不必确保输入字符串中的每个字符都作为键存在于字典中

def letter_changes(string):
    letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVW"
    changes = "bcdEfghIjklmnOpqrstUvwxyzABCDEFGHIJKLMNOPQRSTUVWZ"
    mapping = { k: v for (k, v) in zip(letters, changes) }
    return "".join(mapping.get(c, c) for c in string)

这里mapping.get(c, c)的意思是,“获取与键c相关联的映射,或者如果c不是字典中的键,只需使用c本身”。这意味着字典中没有的' ''!'等符号将保持不变

这一行:

mapping = { k:v for (k,v) in zip(str+letters,str+changes) }

正如您已经观察到的,使用字典理解语法创建字典。生成的字典将每个字母与翻译字符串时要使用的“新”字母相关联。通常是这样做的:

mapping = {k: v for k, v in zip(source, destination)}

甚至更短:

mapping = dict(zip(source, destination))

但是,下一行执行以下操作:

"".join([ mapping[c] for c in str ])

它盲目地转换str中的每个字符,在刚刚创建的字典中进行查找。如果字符串包含映射中未包含的任何字符,则此操作将失败

为了解决这个问题,编写上述代码的人使用了一个愚蠢的技巧,首先将字符串的每个字符添加到映射中,将其与自身关联,然后为要替换的字符添加相应的映射

因此,这里:

mapping = { k:v for (k,v) in zip(str+letters,str+changes) }

letters之前和changes之前的str+将字符串的全部内容预先添加到原始内容和替换内容,为不在letters中的字符串的每个字符创建映射

这与:

mapping = {k: k for k in str}
mapping.update({k: v for k, v in zip(letters, changes)})

不管怎么说,这既糟糕又缓慢,所以要回答你的问题:

why it had to be done?

因为不管是谁写的代码都决定了。不需要它,构建映射需要O(len(str))时间,遍历整个字符串,而实际上不需要。任何Python程序员都不会这样写

“好”的做法是:

mapping = dict(zip(source, destination))
return ''.join(mapping.get(c, c) for c in str)

总而言之,上面的代码相当笨拙,IMHO以一种非常混乱的方式完成了任务

容易发现的问题有:

  1. 映射将迭代整个字符串,这是完全不需要的
  2. 创建映射以替换字符,但不利用Python中已有的^{}^{}内置方法
  3. letters字符串中缺少字母XYZ,因此无法转换
  4. 完全不需要join中的列表理解,它可以在没有方括号的情况下完成[]
  5. 变量名str覆盖全局类型名str,这是错误的,不应该这样做

更好的解决办法是:

def LetterChanges(s):
    old = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    new = 'bcdEfghIjklmnOpqrstUvwxyzAZABCDEFGHIJKLMNOPQRSTUVWXY'
    table = str.maketrans(old, new)
    return s.translate(table)

更好的做法是只预先计算一次表,然后在连续调用中使用已经创建的表:

def LetterChanges(s, table={}):
    if not table:
        old = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
        new = 'bcdEfghIjklmnOpqrstUvwxyzAZABCDEFGHIJKLMNOPQRSTUVWXY'
        table.update(str.maketrans(old, new))

    return s.translate(table)

性能:

  • 原件:1.081s,用于翻译Hello World!的100k译文
  • 更新:100k翻译的Hello World!为0.400s(加速4.5x)
  • 使用缓存更新:0.082s,用于100k的Hello World!翻译(22.5x加速比)

相关问题 更多 >