【问题标题】:Python Map, Lambda, and string.replacePython Map、Lambda 和 string.replace
【发布时间】:2020-06-26 10:37:43
【问题描述】:

我知道如何在不使用 lambda/map 的情况下解决我的问题(而且我不能在这个练习中使用正则表达式或其他 python 库),使用带有 string.replace() 的 for 循环......但我是真的想看看我是否可以使用 map/lambda 和 string.replace 的组合来获得相同的结果\

我的目标是读入一个 txt 文件,然后用字母“e”替换非标准“e”(如 éêèÉÊÈ)的每个实例

我现在的主要问题是我得到了 6 个列表(例如,我在 newFile / newFileListComprehension 中有 6 个字符串,并且每个字符串都已更新原始字符串,基于评估的 1 个可迭代对象

例如newFile[0] = .replace('é') 的输出newFile[1] = .replace(' 的输出ê') 等等

所以我想要的是返回 1 个格式化字符串的副本,所有的 .replace() 都对其进行迭代。

我在下面引用的文本文件的链接可以访问https://easyupload.io/s7m0zj

import string

def file2str(filename):
    with open(filename, 'r', encoding="utf8") as fid:
        return fid.read()

def count_letter_e(filename, ignore_accents, ignore_case):
    eSToAvoid = 'éêèÉÊÈ'
    textFile = file2str("Sentence One.txt")
    newFileListComprehension = [textFile.replace(e,'e') for e in eSToAvoid if ignore_accents == 1]
    value = textFile.count('e')
    #newFile = list((map(lambda element: (textFile.replace(element, 'e') if ignore_accents == 1 else textFile.count('e')), eSToAvoid)))
    return 0

numberOfEs = count_letter_e("Sentence One.txt", 1, 1)```

【问题讨论】:

  • 列表理解很容易从您的代码行中适应。这就是你想要的吗?
  • 老实说,JF,我尝试使用列表理解,但似乎效果更差......而且我认为列表理解会更加困难,因为它本质上将为“每个" 列表中的值....但也许我弄错了?
  • "我认为列表理解会更困难,因为它本质上会为列表中的“每个”值创建一个可迭代的列表。”我不知道这意味着什么,但列表推导本质上是一种方便的方式来编写生成列表的map/filter 操作。
  • 无论如何,你可以提供一个例子,这种模糊的描述通常不会被接受。请注意,数据来自文本文件这一事实并不真正相关。只需提供输入和预期输出,以及您已经使用过的代码。请注意,仅通过阅读您的描述,听起来map 并不是正确的解决方案。 听起来应该是reduce。但实际上,在 Python 中,您只需编写一个 for 循环。即使是map / filter 通常也会在适当的地方避免使用。
  • 当然,juanpa,我更新了我对这个问题的列表理解的“尝试”....我试过了,但被卡住了或多或少与 map/reduce 相同的结果

标签: python string lambda


【解决方案1】:

虽然str.replace 只能用另一个子字符串替换一个子字符串,但re.sub 可以替换一个模式。

In [55]: eSToAvoid = 'éêèÉÊÈ' 
In [58]: import re 

测试用例:

In [61]: re.sub(r'[éêèÉÊÈ]', 'e', 'foobar')                                                                          
Out[61]: 'foobar'
In [62]: re.sub(r'[éêèÉÊÈ]', 'e', eSToAvoid)                                                                         
Out[62]: 'eeeeee'
In [63]: re.sub(r'[éêèÉÊÈ]', 'e', 'testingè,É  foobar  è É')                                                         
Out[63]: 'testinge,e  foobar  e e'

字符串替换的方法是:

In [70]: astr = 'testingè,É  foobar  è É' 
    ...: for e in eSToAvoid: 
    ...:     astr = astr.replace(e,'e') 
    ...:                                                                                                             
In [71]: astr                                                                                                        
Out[71]: 'testinge,e  foobar  e e'

替换顺序应用于astr。这不能表示为列表理解(或map)。列表推导式最自然地替换了将其结果收集到列表中的循环(使用list.append)。

for 循环没有任何问题。它实际上更快:

In [72]: %%timeit 
    ...: astr = 'testingè,É  foobar  è É' 
    ...: for e in eSToAvoid: 
    ...:     astr = astr.replace(e,'e') 
    ...:  
    ...:                                                                                                             
1.37 µs ± 8.96 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [73]: timeit re.sub(r'[éêèÉÊÈ]', 'e', 'testingè,É  foobar  è É')                                                  
2.79 µs ± 15.3 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [77]: timeit astr.translate(str.maketrans(eSToAvoid, 'e' * len(eSToAvoid)))                                       
2.56 µs ± 14.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

减少

In [93]: from functools import reduce  
In [96]: reduce(lambda s,e: s.replace(e,'e'),eSToAvoid, 'testingè,É  foobar  è É' )                                  
Out[96]: 'testinge,e  foobar  e e'
In [97]: timeit reduce(lambda s,e: s.replace(e,'e'),eSToAvoid, 'testingè,É  foobar  è É' )                           
2.11 µs ± 32.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

为了好玩,您还可以探索这里提出的一些想法:

Cleanest way to combine reduce and map in Python

您想要“累积”更改,而要做到这一点,您需要某种累加器,它会挂在最后一次替换上。 itertools 有一个accumulate 函数,Py 3.8 引入了一个:= walrus 运算符。

发电机

In [110]: def foo(astr, es): 
     ...:     for e in es: 
     ...:         astr = astr.replace(e,'e') 
     ...:         yield astr 
     ...:                                                                                                            
In [111]: list(foo(astr, eSToAvoid))                                                                                 
Out[111]: 
['testingè,É  foobar  è É',
 'testingè,É  foobar  è É',
 'testinge,É  foobar  e É',
 'testinge,e  foobar  e e',
 'testinge,e  foobar  e e',
 'testinge,e  foobar  e e']

[s for s in foo(astr, eSToAvoid)] 代替list()。这突出了一个事实,即列表推导返回一个字符串列表,即使字符串累积了更改。

【讨论】:

  • 很好的建议(我的条件之一是不使用正则表达式......)说公平地说我在使用列表理解或 lambda/map 组合时的“尝试”根本没有'不会“工作”来获得我想要的输出吗?
  • 列表推导生成一个列表。这不是你想要的,不是吗?你想要一个链式应用程序,astr.replace(...).replace(...).replace(...)。可能有一些诡计会累积这些变化,但为什么呢?即使在简单的情况下,列表推导也仅比等效循环稍快。
  • 感谢 hpaulj....“为什么”只是反映我对 python 的了解更多...所以我在这里感谢您的洞察力
【解决方案2】:

您可以使用str.translate 一次替换多个字符。 str.maketrans 帮助您创建所需的映射:

eSToAvoid = 'éêèÉÊÈ'
textFile.translate(str.maketrans(eSToAvoid, 'e' * len(eSToAvoid)))

【讨论】:

  • 哦,我的.....谢谢你分享这个方法。这是超级干净,我需要更多地了解它是如何工作的,但它运行良好:) 谢谢 a_guest!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-06-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-31
  • 1970-01-01
相关资源
最近更新 更多