数据的处理和持久化

今天在刷 B 站时看到了码农高天的这一期视频:一个函数能优化五次?这Python写的有C的味道了。虽然标题似乎在暗示过早优化和过度追求性能而牺牲可读性的危害,但我看下来,这个视频的重点还是在数据的处理和持久化,或者说“数据和逻辑分离”。

由于这是一个 Code Review 视频,我们在进入正题前先说明一下在被 review 的代码中出现的问题。

被 review 的代码在此页面可看:extractor_json2csv.py

让我们直入主题,来到第 106 行:

length = str(value).count(':') + 1
appendLength(cur_tcp_stream, str(length))

这里根据输入的 value 以冒号作为分隔符计算长度,再将长度转换为 str 类型传入 appendLength。至于为什么要将长度转换类型?让我们继续看

def appendLength(cur_tcp_stream, value):
    for stream in streams:
        if stream.tcp_stream == cur_tcp_stream:
            if stream.tcp_length is None:
                stream.tcp_length = value
            else:
                stream.tcp_length += ',' + value

这里的 appendLength 函数中将上面的产生的 length 前添加上逗号,添加到一个字符串中。也就是说,这个字符串是像这样的:

1
1,2
1,2,3

正好对应了文件名 json2csv,产生 CSV 格式的数据。其实到这里还看不出什么,让我们再看看后面的代码,当需要从这个长字符串中移除尾部元素时,有如下代码:

def removeLength(cur_tcp_stream, input_string, max_packetlen):
    for stream in streams:
        if stream.tcp_stream == cur_tcp_stream:
            comma_count = 0
            index = 0
            for i, char in enumerate(input_string):
                if char == ',':
                    comma_count += 1
                    if comma_count == max_packetlen:
                        index = i
                        break
            stream.tcp_length = input_string[:index]

这里用了一种很繁琐的方式,将这个 CSV 格式字符串中的元素个数缩减到指定数量。

在经过这样的数据处理后,程序得以直接将这个长字符串写入 CSV 文件中。


由于这个程序的目的是根据输入产生 CSV 文件,程序在处理数据的过程中,始终将数据以 CSV,即逗号分隔的字符串格式存储在内存中。这使得程序代码过于繁琐,可读性也很差。

但是,如果使用“数据和逻辑分离”的原则实现同样的逻辑,则应在处理数据时,使用更易于操作的数据结构存储在内存中。比如对于上面的 length,可以将其存储在一个列表中,由于列表操作方便,使用列表存储可以极大地简化代码,并提高可读性。在处理完数据后,再通过 join 方法生成目标的 CSV 格式字符串,输出到文件。

点此查看原文