根据前缀限制列表中字符串的出现

时间:2018-12-15 21:15:28

标签: python python-3.x irc

所以我正在处理的代码是针对IRC机器人的,我想基于CHANLIMIT服务器选项实现一种限制频道的方法。

CHANLIMIT选项是一个限制列表,其前缀和限制之间用:分隔,但是如果:之后没有任何内容,则没有限制。

下面的解决方案有效,但我正在寻求对此的任何改进。

result = ['#+:2', '&:']
channels = ['#test1', '#test2', '+test3', '&test4']

prefix_groups = [(prefix, []) for prefix in result]
channel_groups = {k: v for (k, v) in prefix_groups}
for channel in channels:
    for group in prefix_groups:
        if channel[0] in group[0]:
            channel_groups[group[0]].append(channel)
            break

for prefix, channels in channel_groups.items():
    limit = prefix.split(':')[1]
    if limit:
        if len(channels) > int(limit):
            channel_groups[prefix] = channels[:int(limit)]

channels = [
    channel for chanlist in channel_groups.values() for channel in chanlist]

print(channels)

5 个答案:

答案 0 :(得分:1)

有很多方法可以解决此问题。进行一些最小的简化,您可能会得到类似的信息:

解决方案1 ​​

results = ['#+:2', '&:']
channels_to_test = ['#test1', '#test2', '+test3', '&test4',
                    '#test5', '!test5', '&test6', '&test7',
                    '+test8', '#test9']

channel_groups = {k: [] for k in results}
for channel in channels_to_test:
    for group in results:
        if channel[0] in group:
            channel_groups[group].append(channel)
            break

for prefix, channels in channel_groups.items():
    limit = prefix.split(':')[1]
    if limit:
        limit = int(limit)
        channel_groups[prefix] = channels[:limit]

result_channels = [
    channel for chanlist in channel_groups.values() for channel in chanlist]

print(result_channels)

这是我所做的更改:

  • 我直接创建了channel_groups,而不是创建元组列表(prefix_groups),然后使用它来创建channel_groups
  • 我在group上迭代了results,而不是在prefix_groups上迭代了
  • 我没有检查是否len(channels) > int(limit),因为即使channels的长度小于或等于limitchannels[:limit]也会返回所有{{ 1}}

答案 1 :(得分:1)

我们可以走得更远:

解决方案2

import itertools

results = ['#+:2', '&:']
channels_to_test = ['#test1', '#test2', '+test3', '&test4',
                    '#test5', '!test5', '&test6', '&test7',
                    '+test8', '#test9']

channel_groups = {group: [channel for channel in channels_to_test
                                  if channel[0] in group]
                  for group in results}

limit = lambda prefix: prefix.split(':')[1]

modified_channel_groups = {prefix: channels[:int(limit(prefix))]
                           for (prefix, channels) in channel_groups.items()
                           if limit(prefix)}

channel_groups.update(modified_channel_groups)

result_channels = list(itertools.chain.from_iterable(channel_groups.values()))

print(result_channels)

但是在这里我必须做一个假设:我认为一个通道最多可以匹配results的一个元素。换句话说,results的两个元素都不会与同一个频道匹配。告诉我这是否不适合您的情况。

这是我所做的更改:

  • 我已经使用字典理解创建了channel_groups,其中每个元素的值都是列表理解
  • 我创建了modified_channel_groups,其中包含已缩短的channel_groups的元素
  • updatedchannel_groups的元素modified_channel_groups
  • 我创建了一个lambda expression,以便可以将其包含在modified_channel_groups的定义中。
  • 我已使用itertools.chain.from_iterable()提取了result_channels

答案 2 :(得分:1)

您甚至可以进一步进行操作,以便直接创建答案channel_groups,但变得更加难以阅读。所以我不推荐它:

解决方案2a

import itertools

results = ['#+:2', '&:']
channels_to_test = ['#test1', '#test2', '+test3', '&test4',
                    '#test5', '!test5', '&test6', '&test7',
                    '+test8', '#test9']

limit = lambda prefix: prefix.split(':')[1]

channel_groups = {group: [channel for channel in channels_to_test if channel[0] in group][:int(limit(group)) if limit(group) else None]
                  for group in results}

result_channels = list(itertools.chain.from_iterable(channel_groups.values()))

print(result_channels)

几件事要注意:

  • channel_groups的创建方式类似于解决方案2 ,但是字典的每个值都是一个列表(从理解中获得),该列表与当前{{1}的整数值相切}或group表示采用所有值。

答案 3 :(得分:1)

当我必须从字符串中提取一些信息时,我倾向于使用regular expressions。因此扩展解决方案2 ,我们可以获得:

解决方案3

import re
import itertools

results = ['#+:2', '&:']
channels_to_test = ['#test1', '#test2', '+test3', '&test4',
                    '#test5', '!test5', '&test6', '&test7',
                    '+test8', '#test9']

prefix_pattern = re.compile(r'^(.*):(\d+)?$')
prefix_matches = (prefix_pattern.match(x) for x in results)
prefix_split = (x.groups() for x in prefix_matches)
channel_groups = {group: [channel for channel in channels_to_test
                                  if channel[0] in group[0]]
                  for group in prefix_split}

prefix_existing_limit = ((x, int(x[1])) for x in channel_groups
                         if x[1] is not None)
modified_channel_groups = {prefix_group: channel_groups[prefix_group][:limit]
                           for (prefix_group, limit) in prefix_existing_limit}

channel_groups.update(modified_channel_groups)

result_channels = list(itertools.chain.from_iterable(channel_groups.values()))

print(result_channels)

答案 4 :(得分:1)

但是让我们备份一下。如果我理解正确,那么最后您需要channels_to_test的元素列表,这些元素与前缀匹配,并且不超过前缀的限制(如果有的话)。您可以在generator中实现此过滤行为:

解决方案4

import re

results = ['#+:2', '&:']
channels_to_test = ['#test1', '#test2', '+test3', '&test4',
                    '#test5', '!test5', '&test6', '&test7',
                    '+test8', '#test9']

def filter_channel_list(prefixes_to_match, input_channel_list):
    prefix_pattern = re.compile(r'^(.*):(\d+)?$')
    prefix_matches = (prefix_pattern.match(x) for x in prefixes_to_match)
    prefix_split = (x.groups() for x in prefix_matches)
    prefixes_remaining = {x: (int(y) if y is not None else None)
                          for (x, y) in prefix_split}

    for current_channel in input_channel_list:
        for (prefix, nb_left) in prefixes_remaining.items():
            if current_channel[0] in prefix:
                if nb_left is None:
                    yield current_channel
                    break
                else:
                    if nb_left > 0:
                        prefixes_remaining[prefix] -= 1
                        yield current_channel
                        break
                    else:
                        continue

result_channels = list(filter_channel_list(results, channels_to_test))

print(result_channels)

以下是一些评论:

  • 在此解决方案中,我提出了一个要求,即channels_to_test的一个元素只能与results的一个元素匹配。这是因为生成器中放置了break语句。
  • 我们要做的是创建一个字典,每个字典的初始限制为results,每次遇到与channels_to_test的元素匹配时递减。如果该值变为0,则生成器将跳至下一个值。这就是continue语句(在这种情况下为可选)的作用。