以下是一些解释的示例代码:
outputText=""
counter=0
for obj in specialObjects:
if (obj.id < 400) or (obj.name.startswith("he")) or (obj.deliberateBreak==True):
print "The object %s is causing a section break."%obj.details
outputText = outputText.rjust(80)
open("file%d.txt"%counter,"w").write(outputText)
outputText=""
outputText+=obj.shortValue()
# THIS CODE IS DUPLICATED
outputText = outputText.rjust(80)
open("file%d.txt"%counter,"w").write(outputText)
我需要做的是迭代这些特殊对象的列表,并每次检查几个不同的条件。如果满足任何条件(如此处所示),那么我需要获取当前输出缓冲区,将其写入文件,然后启动新的输出缓冲区并继续处理。
这里的问题是代码重复。注意两行(outputText =和open)是如何重复的。如果我没有放入第二组行,最后一组对象将被处理,但它们的输出将永远不会被写入。
我可以想到两种可能的解决方案来防止代码重复。它们似乎都略显不雅,所以我想知道是否有更好的方法。
1)包装将在函数中重复的代码。
outputText=""
counter=0
for obj in specialObjects:
if (obj.id < 400) or (obj.name.startswith("he")) or (obj.deliberateBreak==True):
print "The object %s is causing a section break."%obj.details
counter = writeData(outputText)
outputText=""
outputText+=obj.shortValue()
writeData(outputText,counter)
def writeData(outputText,counter):
outputText = outputText.rjust(80)
open("file%d.txt"%counter,"w").write(outputText)
return counter+1
2)改为使用数字for循环,并计数高于对象列表长度的1;使用该值作为标志来表示&#34;写入,但现在退出&#34;:
outputText=""
counter=0
for obj in range(len(specialObjects))+1:
if (obj = len(specialObjects)) or (specialObjects[obj].id < 400) or (specialObjects[obj].name.startswith("he")) or (specialOejcts[obj].deliberateBreak==True):
print "The object %s is causing a section break."%specialObjects[obj].details
outputText = outputText.rjust(80)
open("file%d.txt"%counter,"w").write(outputText)
outputText=""
if (obj==len(specialObjects)):
break
outputText+=specialObjects[obj].shortValue()
如果我必须选择一个,我可能会选择#2,但这最终可能会产生一些奇怪的边缘情况,如果&#39;如果&#39;语句是否需要使用任何更复杂的布尔逻辑。
是否有更清洁或更多的&#34; Pythonic&#34;没有代码重复的方法来实现这一目标?
谢谢!
答案 0 :(得分:2)
当我发现自己编写这样的代码时,我会在循环结束后迭代一个集合并重复代码,我通常把它当作一个标志,表示我没有在右边迭代的事情。
在这种情况下,您将迭代对象列表。但是,我认为真正想要迭代的是一个组对象的列表。 itertools.groupby
对{{3}}有用。
您的代码有很多,所以我将使用一个简化的示例来说明如何摆脱重复的代码。对于(一个非常人为的)例子,我说有一个像这样的事情列表:
things = ["apples", "oranges", "pears", None,
"potatoes", "tomatoes", None,
"oatmeal", "eggs"]
这是一个对象列表。仔细观察,有几组由None
分隔的对象(请注意,您通常将things
表示为嵌套列表,但为了示例的目的,请忽略它们)。我的目标是在一个单独的行上打印出每个小组:
apples, oranges, pears
potatoes, tomatoes
oatmeal, eggs
这里是&#34;丑陋的&#34;这样做的方式:
current_things = []
for thing in things:
if thing is None:
print ", ".join(current_things)
current_things = []
else:
current_things.append(thing)
print ", ".join(current_things)
如您所见,循环后我们有重复的print
。讨厌!
以下是使用groupby
的解决方案:
from itertools import groupby
for key, group in groupby(things, key=lambda x: x is not None):
if key:
print ", ".join(group)
groupby
采用可迭代(things
)和关键函数。它查看iterable的每个元素并应用键函数。当密钥更改值时,将形成一个新组。结果是一个返回(key, group)
对的迭代器。
在这种情况下,我们会使用None
的检查作为我们的关键功能。这就是我们需要if key:
的原因,因为会有一些与我们列表中的None
元素相对应的大小为1的组。我们只是跳过那些。
如您所见,groupby
允许我们迭代我们 想要迭代的事物: group 对象。这对我们的问题更自然,因此代码简化了。看起来你的代码与上面的例子非常相似,只是你的key函数会检查对象的各种属性(obj.id < 400 ...
)。我会将实施细节留给您......
答案 1 :(得分:2)
这是一种使用哨兵对象的方法。它类似于你的第二种选择,但我认为更干净。
for obj in itertools.chain(specialObjects, [None]):
if (obj is None) or (obj.id < 400) or (obj.name.startswith("he")) or (obj.deliberateBreak==True):
outputText = outputText.rjust(80)
open("file%d.txt"%counter,"w").write(outputText)
if obj is None: break
print "The object %s is causing a section break."%obj.details
outputText=""
outputText+=obj.shortValue()
答案 2 :(得分:1)
您可以将分解对象的代码分隔为生成器,以便不需要复制后面的处理步骤。
def yield_sections(specialObjects):
outputText = ''
for obj in specialObjects:
if (obj.id < 400) or (obj.name.startswith("he")) or (obj.deliberateBreak==True):
yield outputText
outputText = ''
outputText += obj.shortValue()
if outputText:
yield outputText
for counter, outputText in enumerate(yield_sections(specialObjects)):
outputText = outputText.rjust(80)
open("file%d.txt"%counter,"w").write(outputText)
答案 3 :(得分:1)
如果使用迭代器,有一个解决方案,next
可以在最后给出一个特殊值。因此,您可以使用标记来检查当前对象是否为真对象,或者是否完成了迭代。
尝试这样的事情:
outputText=""
counter=0
ending = object()
it = iter(specialObjects)
while True:
obj = next(it, ending)
if obj is ending or obj.id < 400 or obj.name.startswith("he") or obj.deliberateBreak:
outputText = outputText.rjust(80)
open("file%d.txt"%counter,"w").write(outputText)
counter += 1
outputText=""
if obj is ending:
break
outputText+=obj.shortValue()