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

Per*_*eus 0 python python-3.x

我正在经历一些 Python 挑战,这个特别的挑战一直困扰着我,并认为值得解释一下。它写道:

让函数 LetterChanges(str) 获取传递的 str 参数并使用以下算法对其进行修改。用字母表中紧随其后的字母替换字符串中的每个字母(即,c 变为 d,z 变为 a)。然后将这个新字符串 (a, e, i, o, u) 中的每个元音大写,最后返回这个修改后的字符串。

例子:

Input: "fun times!"
Output: gvO Ujnft!
Run Code Online (Sandbox Code Playgroud)

编码:

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 ])
Run Code Online (Sandbox Code Playgroud)

我知道它需要两个字符串、字母和更改。它使用 zip() 函数接受迭代器并“压缩”它们,形成字典形式的迭代器。k:v for (k,v)这是一个字典理解。

我的疑问是:

究竟发生了什么str+letters,str+changes以及为什么必须这样做?

[ mapping[c] for c in str ] 为什么通过这样做,我们完成了用其值替换每个键或在挑战描述中说:“用字母表中紧随其后的字母替换字符串中的每个字母”

Mar*_*lli 5

这一行:

mapping = { k:v for (k,v) in zip(str+letters,str+changes) }
Run Code Online (Sandbox Code Playgroud)

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

mapping = {k: v for k, v in zip(source, destination)}
Run Code Online (Sandbox Code Playgroud)

或者更短:

mapping = dict(zip(source, destination))
Run Code Online (Sandbox Code Playgroud)

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

"".join([ mapping[c] for c in str ])
Run Code Online (Sandbox Code Playgroud)

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

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

所以在这里:

mapping = { k:v for (k,v) in zip(str+letters,str+changes) }
Run Code Online (Sandbox Code Playgroud)

str+之前letters和前changes预先将字符串原件和替代两者的全部内容,创造,是不是在字符串中的每个字符映射letters

这与:

mapping = {k: k for k in str}
mapping.update({k: v for k, v in zip(letters, changes)})
Run Code Online (Sandbox Code Playgroud)

无论如何,这既糟糕又缓慢,所以回答你的问题:

为什么必须这样做?

因为写代码的人决定了。没有必要,它需要O(len(str))时间来构建映射,遍历整个字符串,而实际上没有必要。没有 Python 程序员会这样写。

这样做的“好”方法是:

mapping = dict(zip(source, destination))
return ''.join(mapping.get(c, c) for c in str)
Run Code Online (Sandbox Code Playgroud)

总而言之,上面的代码非常笨拙,恕我直言,以非常混乱的方式完成了任务。

容易发现的问题是:

  1. 映射遍历整个字符串,这是完全不需要的。
  2. 创建一个映射来替换字符,但不利用Python 中现有的str.maketrans()str.translate()内置的方法。
  3. 字符串中缺少字母X, Y,因此未转换。Zletters
  4. 里面的列表join推导完全不需要,不用方括号也可以[]
  5. 变量 namestr覆盖了全局类型 name str,这是不好的,不应该这样做。

更好的解决方案是:

def LetterChanges(s):
    old = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    new = 'bcdEfghIjklmnOpqrstUvwxyzAZABCDEFGHIJKLMNOPQRSTUVWXY'
    table = str.maketrans(old, new)
    return s.translate(table)
Run Code Online (Sandbox Code Playgroud)

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

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

    return s.translate(table)
Run Code Online (Sandbox Code Playgroud)

表现:

  • 原文:1.081s 的 100k 翻译Hello World!
  • 更新:10 万次翻译Hello World!(4.5 倍加速)需要0.400 秒。
  • 更新缓存:100k 翻译Hello World!(22.5 倍加速)需要 0.082 秒。