python - 递归列表理解以在列表上应用lstrip

时间:2016-02-08 05:56:12

标签: python regex list recursion list-comprehension

我有两个列表如下

f = ['sum_','count_','per_']
d = ['fav_genre','sum_fav_event','count_fav_type','per_fav_movie']

所以我想在列表d的所有项目中应用f中每个字符串的lstrip,以便我可以得到

d = ['fav_genre','fav_event','fav_type','fav_movie']

我想用列表理解来做。 但我知道我也可以用其他方式来做,比如使用re.sub,每次在d列表项上应用replace

 #example
 d = [re.sub(r'.*fav', 'fav', x) for x in d] #####gives what i want
 ## but if fav (which in this case a matching pattern) is not there in d then this solution won't work
 ## d = ['fav_genre','sum_any_event','count_some_type','per_all_movie']
 #re.sub can't be applied on this d(as before) as no matching char like 'fav' found 

所以列表压缩是我选择做的..

到目前为止,我已经尝试过..

d_one = [x.lstrip('count_') for x in d]   ###only count_ is stripped
# o/p- d-one = ['fav_genre', 'sum_fav_event', 'fav_type', 'per_fav_movie']
# so i c_n apply lstrip of each string from f on items of d
## why not apply all items lstrip in one go ### so tried
d_new = [x.lstrip(y) for y in f for x in d]
###['fav_genre', 'fav_event', 'count_fav_type', 'per_fav_movie', 'fav_genre', 'sum_fav_event', 'fav_type', 'per_fav_movie', 'fav_genre', 'sum_fav_event', 'count_fav_type', 'fav_movie']

所以它给了我lstrip应用的每次迭代的结果

请建议我如何在列表理解中逐步应用所有lstrip(递归)。在此先感谢。

5 个答案:

答案 0 :(得分:3)

试试这个:

>>> f = ['sum_','count_','per_']
>>> d = ['fav_genre','sum_fav_event','count_fav_type','per_fav_movie']
>>> [s[len(([p for p in f if s.startswith(p)]+[""])[0]):] for s in d]
['fav_genre', 'fav_event', 'fav_type', 'fav_movie']

我相信这可以按预期处理所有情况。

答案 1 :(得分:3)

可以使用以下方法,根据f

创建合适的正则表达式
import re

f = ['sum_','count_','per_']
d = ['fav_genre','sum_fav_event','count_fav_type','per_fav_movie']

re_prefix = re.compile(r'^({})'.format('|'.join(f)))
print [re_prefix.sub('', entry) for entry in d]

或者作为一个单行(不那么有效):

print [re.sub(r'^({})'.format('|'.join(f)), '', entry) for entry in d]

给你以下输出:

['fav_genre', 'fav_event', 'fav_type', 'fav_movie']

答案 2 :(得分:1)

这就是你要找的东西?

>>> f = ['sum_','count_','per_']
>>> d = ['fav_genre','sum_fav_event','count_fav_type','per_fav_movie']
>>> [x[len(y):] for x in d for y in f if x.startswith(y)]
['fav_event', 'fav_type', 'fav_movie']

编辑: 我越是嘲笑这一点,我就越发现列表理解不可能。问题似乎包括不匹配的条件,但是一个简单的“其他”条件。当迭代f中的其他项目时,会导致d中的每个项目被包括在内。

例如

>>> [x[len(y):] if x.startswith(y) else x for x in d for y in f
['fav_genre', 'fav_genre', 'fav_genre', 'fav_event', 'sum_fav_event', 'sum_fav_event', 'count_fav_type', 'fav_type', 'count_fav_type', 'per_fav_movie', 'per_fav_movie', 'fav_movie']

这会创建一个包含太多项目的新列表。

向列表comp添加另一个条件会生成语法错误:

[x[len(y):] if x.startswith(y) else x if x[len(y):] not in f for x in d for y in f]
File "<stdin>", line 1
  [x[len(y):] if x.startswith(y) else x if x[len(y):] not in f for x in d for y in f]
                                                               ^
SyntaxError: invalid syntax

即使我们能够通过列表理解来实现这一点,函数也会更具可读性:

def strip_prefixes(prefixes, mylist):
    for element in mylist:
        for x in prefixes:
            if element.startswith(x):
                element = element[len(x):]
    return mylist

答案 3 :(得分:1)

不要太多地使用列表理解来做这件事。列表理解,如果非常像map / reduce语法糖。通过使用简单的函数,您将获得更容易阅读的解决方案。

import re

f = ['sum_','count_','per_']
d = ['fav_genre','sum_fav_event','count_fav_type','per_fav_movie']
def makeTrimmer(patterns):
    regex = re.compile("^(%s)" % "|".join(patterns))

    def trimmer(string):

        old_string = string             
        new_string = re.sub(regex, "", old_string)

        while len(old_string) != len(new_string):
            old_string = new_string
            new_string = re.sub(regex, "", old_string)

        return new_string

    return trimmer

trimmer = makeTrimmer(f)
vals = [trimmer(x) for x in d]
print vals

正如您所看到的,trimmer函数非常易读,您可以在列表解析中执行此操作,但没有简单的方法可以做到这一点。因为列表推导的if部分非常类似于要输出的事物列表上的过滤器。 for部分是组合条目,第一部分是构建条目输出。在您的情况下,您只需要根据多个前缀构建正确的输出...换句话说,您不是要尝试将所有前缀与所有值组合成多个输出,而您不会过滤任何结果。

我的方法可能可以通过lambdas实现,但这很可能是丑陋的。

没有lambda的非递归方法:

vals = [
    re.sub(re.compile("^(%s)" % "|".join(f)), "", x)
    for x in d
]                      
print vals

这是使用匿名lambdas的完整递归:

# -*- coding: utf-8 -*-
import re

f = ['sum_','count_','per_']
d = ['fav_genre','sum_fav_event','count_fav_type','per_fav_movie']

vals = [
    (lambda a, *b: a(a, *b))(
        (lambda loop, newstring, oldstring:
            newstring
            if len(newstring) == len(oldstring) else
                loop(
                    loop,
                    newstring,
                    re.sub(re.compile("^(%s)" % "|".join(f)), "", x)
                )
        ),
        re.sub(re.compile("^(%s)" % "|".join(f)), "", x),
        x
    )
    for x in d
]

print vals

这与上述方法基本相同,只是我们使用递归方法调用进一步过滤,因此此方法将sum_count_per_fun_avg之类的内容清除为fun_avg

另外,不要使用lambda方法,效率低下。

但这是一个更高效的lambda版本:

vals = [
    (lambda regex:
        (lambda a, *b: a(a, *b))(
            (lambda loop, newstring, oldstring:
                newstring
                if len(newstring) == len(oldstring) else
                    loop(                                                                                                                   
                        loop,
                        newstring,
                        re.sub(regex, "", x)
                    )
            ),
            re.sub(regex, "", x),
            x
        )
    )(re.compile("^(%s)" % "|".join(f)))
    for x in d
]

我们只编译一次正则表达式。但是python中的递归仍然是一个问题所以你不应该使用递归。

答案 4 :(得分:1)

我准备去睡觉,但正在努力。我认为这样做可能不是最好的主意,因为它是很多循环并且不那么可读。这也不太对。

d_new = set([(x,y) for x in [x.split(y)[1] for y in f for x in d if x.startswith(y)] for y in [x for x in d if x.startswith('fav')]])

它目前将它们放入元组中,您可以在集合中为x添加另一个x来提取不同的元组对。在这一点上虽然我甚至认为使用列表理解它是有用的或值得的,但如果你真的想使用它,这可能会给你一个开始。

修改

代码看起来像这样:

  

[('fav_movie','fav_genre'),('fav_event','fav_genre'),('fav_type',   'fav_genre')]