【问题标题】:Maps and List Comprehension in PythonPython中的地图和列表理解
【发布时间】:2017-11-25 00:36:18
【问题描述】:

所以我对列表理解和 map/filter/reduce 的概念相当陌生,但觉得这可以用几行和更少的标识来完成:

ip_tlsv1_counts = {}

for filename in os.listdir(directory_path):
    if filename.endswith(LOG_FILE_EXTENSION):
        with open(os.path.join(directory_path, filename)) as file_handle:
            for line_contents in file_handle:
                line_groups = re.search(LOG_LINE_REGEX, line_contents)
                if line_groups and line_groups.group(8) == "TLSv1":
                    if not line_groups.group(2) in ip_tlsv1_counts:
                        ip_tlsv1_counts[line_groups.group(2)] = 1
                    else:
                        ip_tlsv1_counts[line_groups.group(2)] += 1

return ip_tlsv1_counts

【问题讨论】:

  • 那么,它有什么作用?
  • 您将通过使用几个函数来减少缩进并提高清晰度。我怀疑理解将是你的救命之恩。
  • 您可以使用Counter 而不是dict 来代替ip_tlsv1_counts,并将您的内部if-else 替换为简单的ip_tlsv1_counts[line_groups.group(2)] += 1
  • 您可以通过迭代log_files = (f for f in os.listdir(directory_path) if f.endswith(LOG_FILE_EXTENSION) 轻松删除缩进块,然后对其进行迭代:for file in log_files:
  • @Coldspeed 这使用一个正则表达式来匹配文件中的日志条目并计算具有“TLSv1”字段的 IP 地址的出现次数

标签: python dictionary filter list-comprehension reduce


【解决方案1】:

我会使用一个生成器函数来遍历特定文件夹中具有特定扩展名的所有文件:

def filter_files(directory, extension):
    for filename in os.listdir(directory):
        if filename.endswith(extension):
            with open(os.path.join(directory, filename)) as file_handle:
                yield file_handle

然后遍历所有这些文件的所有行,您只需在该生成器的结果上使用itertools.chain.from_iterable

接下来,您可以使用re.compile(LOG_LINES_REGEX) 来获得编译模式,这会提高性能,并且可以在map 中使用它的.search 方法:

log_line_re = re.compile(LOG_LINE_REGEX)
all_log_lines = itertools.chain.from_iterable(filter_files(directory_path, LOG_FILE_EXTENSION))

for line_groups in map(log_line_re.search, all_log_lines):
    if line_groups and line_groups.group(8) == "TLSv1":
        yield line_groups.group(2)

这将是一个生成器,可生成与其他条件匹配的所有 line_groups.group(2),因此要计算所有频率,您只需构造一个带有其结果的 Counter

所以最终的代码应该是这样的:

def filter_files(directory, extension):
    for filename in os.listdir(directory):
        if filename.endswith(extension):
            with open(os.path.join(directory, filename)) as file_handle:
                yield file_handle

def get_part_of_log_files():
    log_line_re = re.compile(LOG_LINE_REGEX)
    all_log_lines = itertools.chain.from_iterable(filter_files(directory_path, LOG_FILE_EXTENSION))

    for line_groups in map(log_line_re.search, all_log_lines):
        if line_groups and line_groups.group(8) == "TLSv1":
            yield line_groups.group(2)

def original_function():
    return collections.Counter(get_part_of_log_files())

【讨论】:

  • 我喜欢这个,因为它分解了功能并且看起来可重复使用,尽管它与我好奇的大致相同的行数和缩进 - 感谢您的回复!
【解决方案2】:

如果你使用 python 3.4+,你可以使用pathlib 模块:

from pathlib import Path
from collections import Counter

ip_tlsv1_counts = Counter()

for path in Path(directory_path).glob('*' + LOG_FILE_EXTENSION):
    with path.open() as f1:
        for line in f1:
            line_groups = re.search(LOG_LINE_REGEX, line)
            if line_groups and line_groups.group(8) == "TLSv1":
                ip_tlsv1_counts[line_groups.group(2)] += 1

return ip_tlsv1_counts

【讨论】:

  • 这很性感,但根据我的经验,python 3.4+ 是一个苛刻的要求 :) 谢谢你的回复 - 这绝对是更性感的方式
猜你喜欢
  • 1970-01-01
  • 2011-09-02
  • 2010-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-05
相关资源
最近更新 更多