Python | 对比两段计数器后想到的

credit by Markus Spiske

任务背景:有 n 个 txt 文件,每个文件都有 i 行,每行的内容就是对应的行号 j。现在需要按照一定的间隔(slice_size),连续切分这 n 个文件,并返回切分点对应位置的总数、切分点在文件中对应行号、从该切分点开始能读取多少数据。

三个示例文件如下,三个文件的长度分别为 12,4 和 5,总共 21 行数据。

返回:[(1, 1, 5), (6, 6, 5), (11, 11, 2), (13, 1, 4), (17, 1, 5)]

文件一:

1
2
3
4
5
6
7
8
9
10
11
12
 1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 10
11 11
12 12

文件二:

1
2
3
4
1 1
2 2
3 3
4 4

文件三:

1
2
3
4
5
1 1
2 2
3 3
4 4
5 5

我认为,问题处理的难点在于最后一个返回值,我们需要在遍历完文件之后才能知道切分点后又多少数据。比如第一个文件,按照每 5 行切分一次,最后一次切分点在第 11 行,而此时,指针到文件末尾时我才知道从 11 这个切分点开始只剩下了两个数据,我无法提前知道剩下多少个数据。在这个地方我纠结了很久,到底怎样才能保存好这个值呢?我能确定的是,只有当指针到达文件末尾时,最后一次切分才会结束,才会知道在该次切分中剩余多少数据,所以,在 counter_01 中,我加入了一个 if not line_pos: 的判断条件。

接下来就是计算剩下多少数据的问题了,我通过 res = idx % slice_size 得到剩余的数据,比如总数 12 的文件,以 5 来切分,12 % 5 = 2,这样就得到了最后一次需要读取的数据。但是,这种做法存在一个很严重的问题:每次切分时我都是默认切分点后面会有 slice_size 个数据,所以当指针到达 11 行时,我就提前假设后面有了 5 条数据,然而事实上并没有,需要当指针到达最后一行时来修正。为了掩饰那个错误,我用了比较暴力的办法:直接干掉列表的倒数第二个自以为正确的数据。

对于第一个返回值,切分点在三个文件累计位置,只需要在文件遍历的最外层设置 total 计数变量即可,每读完一行数据再加一。这里也有一个需要处理的坑,比如在 if not line_pos: 的判断中,切分点累计位置如果还等于 total,那最后一个切分点的位置永远会在文件的最后一行,所以需要在 if idx % slice_size == 0: 中先把 total 赋值给 start_num 存下来给接下来的判断用。第二个返回值处理的逻辑也类似,把 line_pos 赋值给 tmp_line_pos,留给接下来的判断语句中使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/usr/bin/python
# -*- coding=utf8 -*-
"""
# @Author : Li Bin
# @Created Time : 2018-08-15 23:32:55
# @Description : 返回切分列表
"""

slice_size = 5

file_paths = ['/home/libin/cook_book/dataset/test_01.txt',
'/home/libin/cook_book/dataset/test_02.txt',
'/home/libin/cook_book/dataset/test_03.txt']

def counter_01():
total = 0
slice_list = []
for file_path in file_paths:
with open(file_path) as fp:
line_pos = fp.readline()
idx = 0
while line_pos:
total += 1

if idx % slice_size == 0:
start_num = total
slice_list.append((start_num, int(line_pos.strip()), slice_size))
tmp_line_pos = line_pos

line_pos = fp.readline()

if not line_pos:
last_idx = (idx / slice_size) * slice_size
res = idx % slice_size
slice_list.append((start_num, int(tmp_line_pos.strip()), res+1))
del slice_list[-2]
idx += 1
print slice_list

第一个计数器勉强实现了计数的功能,但是不够漂亮,特别是处理剩余数据的逻辑上有说不出的混乱和复杂,很蹩脚。来看第二个计数器,初始参数有 totalslice_counter 两个,对比计数器一,计数器二的思路为每次只切一份小片数据,slice_counter 等于 1,记下 slice_start_posslice_start_num,当 slice_counter 等于 5,将上一个 slice_start_posslice_start_num 取出来,此时该小片有 5 个数据。

那么问题来了,要是最后一个分片没有 5 行数据咋办?这里的处理逻辑很漂亮,当切完一个片,slice_counter 归零,当指针到达了最后一行,如果最后一个分片不足 5,那么 slice_counter 是不会归零的!while 判断跳出来后,后面加个 if slice_counter > 0: 把剩余的 slice_start_posslice_start_numslice_counter 取出来,任务完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def counter_02():
total = 0
slice_list = []
for file_path in file_paths:
with open(file_path) as fp:
line_pos = fp.readline()
slice_counter = 0
while line_pos:
total += 1
slice_counter += 1

if slice_counter == 1:
slice_start_pos = int(line_pos.strip())
slice_start_num = total

if slice_counter == slice_size:
slice_list.append((slice_start_num , slice_start_pos, slice_counter))
slice_counter = 0

line_pos = fp.readline()

if slice_counter > 0:
slice_list.append((slice_start_num, slice_start_pos, slice_counter))
print slice_list

对比以上两个计数器,显然第二个更优,处理的逻辑干净、思路清晰,每个变量负责的任务很明确,特别是最后的 if slice_counter > 0: 很漂亮。第一个计数器在一开始想干的事情太多了,一开始就想着假设后面有 5 条数据,然后靠后面的暴力删除来掩饰这个错误。对比完这两个计数器,我觉得,在处理的问题的时候,拆解问题的能力很重要,一个逻辑处理一个小问题,别想着存在一个万能逻辑解决多个问题,这是我以上想到的。

继续加强代码能力。。。

觉得还不错?赞助一下~
0%