<p>如前所述,您需要某种类型的分块。我们也需要完全忽略文件中不相关的行。我已经用下面一些不错的Python巫术实现了这样一个函数。在</p>
<p>它也可能适合使用namedtuple来存储值。namedtuple是一种非常简单的对象类型,它只存储一些不同的值——例如,2D空间中的一个点可能是一个带有x和y字段的namedtuple。这是<a href="https://docs.python.org/3/library/collections.html#collections.namedtuple" rel="nofollow noreferrer">Python documentation</a>中给出的示例。如果您愿意的话,您应该参考该链接以了解有关namedtuples及其用法的更多信息。我已经使用字段<code>["number", "title", "weight", "fullMark", "desc"]</code>创建了一个任务类。在</p>
<p>由于变量都是任务的属性,所以为了简洁明了,使用命名元组可能是有意义的。在</p>
<p>除此之外,我试着坚持你的方法,用结肠分开。我的代码生成输出</p>
<pre><code>================================================================================
number is 210CT1
title is Assignment 1
weight is 25
fullMark is 100
desc is Program and design and complexity running time.
================================================================================
number is 210CT2
title is Assignment 2
weight is 25
fullMark is 100
desc is Shortest Path Algorithm
================================================================================
number is 210CT3
title is Final Examination
weight is 50
fullMark is 100
desc is Close Book Examination
</code></pre>
<p>这似乎是你想要的-我不知道你的输出要求有多严格。不过,为了达到这个目的,应该相对容易修改。在</p>
<p>下面是我的代码,并给出了一些解释性注释:</p>
^{pr2}$
<p>也可以引用任务的每个字段,如下所示:</p>
<pre><code> print("The number is {}".format(task.number))
</code></pre>
<p>如果不需要namedtuple方法,可以随意将main for循环的内容替换为</p>
<pre><code> taskNumber, taskTitle, weight, fullMark, desc = (line.strip().split(": ")[1] for line in task_lines)
</code></pre>
<p>然后你的代码就会恢复正常。在</p>
<p>关于我所做的其他更改的一些注释:</p>
<p><code>filter</code>只在满足谓词(<code>line_is_relevant(line)</code>是<code>True</code>)的行上迭代它在tin上所说的。在</p>
<p>任务实例化中的<code>*</code>解压迭代器,因此每一个解析的行都是任务构造函数的一个参数。在</p>
<p>表达式<code>(line.strip().split(": ")[1] for line in task_lines)</code>是一个生成器。这是必需的,因为我们使用<code>task_lines</code>同时执行多行,因此对于“chunk”中的每一行,我们将其剥离,用冒号分割,并获取第二个元素,即值。在</p>
<p>{{{cd2}对相同函数的引用{cda2}对相同函数的引用{cda2}起作用。<code>zip</code>然后尝试从该列表的每个元素生成下一个元素,但是由于n个元素中的每一个都是文件的迭代器,<code>zip</code>生成文件的n行。直到迭代器耗尽为止。在</p>
<p><code>line_is_relevant</code>函数使用了“真实性”的概念。一种更详细的实现方法可能是</p>
<pre><code>def line_is_relevant(line):
return len(line.strip()) > 0 and line[0] != '#'
</code></pre>
<p>然而,在Python中,每个对象都可以隐式地用于布尔逻辑表达式。在这样的表达式中,空字符串(<code>""</code>)充当<code>False</code>,而非空字符串充当<code>True</code>,因此,如果{<cd16>}为空,它将充当<code>False</code>,<code>line_is_relevant</code>因此将是{<cd14>}。如果第一个操作数是falsy,<code>and</code>运算符也将短路,这意味着第二个操作数将不会被计算,因此,方便地引用<code>line[0]</code>不会导致{<cd22>}。在</p>
<p>好的,下面是我对<code>n_lines function</code>更广泛的解释:</p>
<p>首先,<code>zip</code>函数允许您一次迭代多个'<code>iterable</code>'。iterable类似于列表或文件,可以在for循环中查看,因此zip函数可以让您执行以下操作:</p>
<pre><code>>>> for i in zip(["foo", "bar", "baz"], [1, 4, 9]):
... print(i)
...
('foo', 1)
('bar', 4)
('baz', 9)
</code></pre>
<p><code>zip</code>函数一次从每个列表中返回一个元素的“<code>tuple</code>”。元组基本上是一个列表,但它是不可变的,所以您不能更改它,因为zip并不希望您更改它给您的任何值,而是希望您对它们做些什么。元组和普通的列表很相似。现在这里有一个有用的技巧是使用“unpacking”来分离元组的每个位,如下所示:</p>
<pre><code>>>> for a, b in zip(["foo", "bar", "baz"], [1, 4, 9]):
... print("a is {} and b is {}".format(a, b))
...
a is foo and b is 1
a is bar and b is 4
a is baz and b is 9
</code></pre>
<p>一个更简单的解包示例,您可能已经看到过了(Python还允许您在这里省略括号()):</p>
<pre><code>>>> a, b = (1, 2)
>>> a
1
>>> b
2
</code></pre>
<p>尽管<code>n-lines function</code>没有使用这个。现在<code>zip</code>也可以处理多个参数-您可以压缩三个,四个或任意多个列表。在</p>
<pre><code>>>> for i in zip([1, 2, 3], [0.5, -2, 9], ["cat", "dog", "apple"], "ABC"):
... print(i)
...
(1, 0.5, 'cat', 'A')
(2, -2, 'dog', 'B')
(3, 9, 'apple', 'C')
</code></pre>
<p>现在,<code>n_lines</code>函数将<code>*[iter(read_file)] * n</code>传递给<code>zip</code>。这里有几件事要讲——我将从第二部分开始。请注意,第一个<code>*</code>的优先级低于后面的所有内容,因此它等价于<code>*([iter(read_file)] * n)</code>。现在,<code>iter(read_file)</code>所做的是通过调用<code>iter</code>从{<cd36>}构造一个迭代器对象。迭代器有点像一个列表,只是你不能索引它,比如<code>it[0]</code>。你所能做的就是“迭代它”,就像在for循环中遍历它一样。然后,它将这个迭代器作为它唯一的元素来构建一个长度为1的列表。然后它将这个列表“乘以”<code>n</code>。在</p>
<p>在Python中,使用*运算符和一个列表将其连接到自身<code>n</code>次。如果你仔细考虑一下,这是有意义的,因为<code>+</code>是串联运算符。比如说</p>
<pre><code>>>> [1, 2, 3] * 3 == [1, 2, 3] + [1, 2, 3] + [1, 2, 3] == [1, 2, 3, 1, 2, 3, 1, 2, 3]
True
</code></pre>
<p>顺便说一下,这使用了Python的链式比较运算符-<code>a == b == c</code>相当于<code>a == b and b == c</code>,只是b只需要计算一次,这在99%的情况下都不重要。在</p>
<p>总之,我们现在知道*操作符复制了一个列表n次。它还有一个属性-它不构建任何新对象。这可能有点让人捉摸不透-</p>
<pre><code>>>> l = [object()] * 3
>>> id(l[0])
139954667810976
>>> id(l[1])
139954667810976
>>> id(l[2])
139954667810976
</code></pre>
<p>这里l是三个<code>object</code>s,但实际上它们都是同一个对象(您可能会认为这是指向同一对象的三个“指针”)。如果要构建一个包含更复杂对象(如列表)的列表,并执行诸如排序之类的就地操作,则会影响列表中的所有元素。在</p>
<pre><code>>>> l = [ [3, 2, 1] ] * 4
>>> l
[[3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1]]
>>> l[0].sort()
>>> l
[[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]
</code></pre>
<p>所以<code>[iter(read_file)] * n</code>相当于</p>
<pre><code>it = iter(read_file)
l = [it, it, it, it... n times]
</code></pre>
<p>现在,第一个<code>*</code>,优先级较低的,再次“解包”这个,但这次并没有将它赋给一个变量,而是赋给<code>zip</code>的参数。这意味着<code>zip</code>将列表的每个元素作为一个单独的参数接收,而不仅仅是列表中的一个参数。下面是一个在更简单的情况下如何打开包装的示例:</p>
<pre><code>>>> def f(a, b):
... print(a + b)
...
>>> f([1, 2]) #doesn't work
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f() missing 1 required positional argument: 'b'
>>> f(*[1, 2]) #works just like f(1, 2)
3
</code></pre>
<p>我们现在有了效果</p>
<pre><code>it = iter(read_file)
return zip(it, it, it... n times)
</code></pre>
<p>请记住,当你在for循环中对一个file对象进行“迭代”时,你会迭代文件的每一行,所以当zip试图一次“遍历”n个对象中的每一个时,它会从每个对象中绘制一行-但是由于每个对象都是相同的迭代器,所以这行被“消耗”,它绘制的下一行是文件的下一行。从它的n个参数中的每一个进行一轮迭代,生成n行,这就是我们想要的。在</p>