我一直在练习Python练习问题,并遇到了这个问题:

编写一个Python程序,在给定字符串的末尾添加“ ing”(长度至少应为3)。
如果给定的字符串已经以'ing'结尾,则添加'ly'。
如果给定的字符串的字符串长度小于3,则保持不变。
。示例字符串:'abc'
预期结果:'abcing'
示例字符串:'string'
预期结果:'stringly'

这是我的代码:
string = input()
if len(string) < 3:
  print(string)
elif string[-3:] == 'ing':
  print(string + 'ly')
else:
  print(string + 'ing')

但是,关于此代码的某些问题似乎是错误的:尽管它可以工作,但是我觉得它闻起来很不好。我无法查明原因。

#1 楼

1. Python字符串具有.endswith()方法。使用它。
虽然string[-3:] == 'ing'并不是特别难读,但string.endswith('ing')甚至更清晰。
可能,根据实现方式的不同,.endswith()甚至可能稍微更高效,因为它不必复制字符串的最后三个字符。但是,请始终牢记Knuth的格言。测试字符串是否以“ ing”结尾的整个操作非常琐碎,以至于与例如读取和打印字符串所花费的时间。因此,有两种方法可以做到,即使不是最有效的方法,也应该选择最易读和惯用的方法。
2。将I / O与计算分开。
对于新手来说,在程序逻辑中间使用input()print()调用填充代码是很常见的,这仅仅是因为它们是接收和返回他们熟悉的数据的唯一方法。但是实际上,这几乎总是一个错误。(*)
您的主要任务是修改字符串。从标准输入中读取字符串并将结果打印到标准输出仅是该任务的辅助任务(并且可能甚至可能不是必须的,除非练习明确指定了特定的输入和输出方法)。您不应该将这些辅助任务不必要地混入主任务的解决方案中。
最简单的方法是将print()调用从程序逻辑中拉出,例如像这样:
 string = input()

if string.endswith('ing'):
  string += 'ly'
elif len(string) >= 3:
  string += 'ing'

print(string)
 

如果您希望仅使用此代码来处理从一个文件(或管道)读取并写入另一个文件的数据,则可以这样做。您仍在一个线性程序中进行输入,处理和输出,但是至少现在将处理逻辑分组到一个代码块中,如果要重用它,可以从脚本中提取该代码,而I / O代码与它明确分开,因此如果需要,可以很容易地在以后更改为其他I / O方法。
或者,您可以更进一步,将程序逻辑包装在采用输入字符串的函数中作为参数并返回输出。而且,在使用时,可以使用if __name__ == '__main__'惯用法来使I / O代码仅在直接调用脚本时运行:
 def inglify(string):
  if len(string) < 3:
    return string
  elif string.endswith('ing'):
    return string + 'ly'
  else:
    return string + 'ing'

if __name__ == '__main__':
  string = input()
  print(inglify(string))
 

优点是,现在您的脚本文件也可用作模块,您可以在不执行代码的I / O部分的情况下,将其import插入另一个脚本中,以访问其中定义的任何功能。 />
*)那么,什么时候应该在代码中直接包含print()调用呢?我不禁想到了三种或四种情况(它们之间有些部分重叠):


当I / O是代码的主要目的时,一切与这个目的不直接相关的功能已经分解为可重用的功能。例如,print(inglify(input()))实际上只做任何事情,但读取数据,调用函数并打印结果。显然,使用print()不仅合适而且必要。


当您的代码是交互式的时,即当它向控制台打印某些内容,希望用户进行响应,然后打印一些响应内容时,等等。您可以通过不带脚本文件参数运行python来启动的REPL就是这样一个程序的示例。在这种情况下,输入和输出本质上是程序逻辑的一部分,不能轻易与之分离。也就是说,您仍应尝试使此类交互式循环尽可能简单,并将不直接涉及交互式I / O的代码的任何部分拆分为可重用的函数或模块。 br />进行调试打印时,即打印的内容不是程序的实际预期输出,而只是一些元数据来帮助跟踪其执行情况。有时这可能是一种非常有用的方法,可以了解您的代码实际在做什么,并且在这种情况下,甚至可以将print()或其他I / O调用放在程序逻辑的内部也可以接受。当然,如果您要在完成的代码中保留此类调试日志,则仅应在用户请求时才启用它。 (例如,许多命令行工具将接受--verbose--debug开关以打开此类额外输出。)通常,最好使用为此目的而设计的实际日志记录框架。


我个人还会为“简单数据处理”脚本添加一个例外,该脚本只涉及读取,处理和打印数据,就像我上面建议的代码的第一个重写版本一样。
此类脚本的特征在于,它们仅执行一个任务,该任务涉及读取,处理和写入数据,并且它们的控制流程简单且大多是线性的。也就是说,这样的脚本可能包含一个读取输入的循环,另一个或两个处理该输入的循环,然后是一个打印结果的循环。有时甚至可能会有一个循环,例如读取输入的一行,对其进行处理,并在读取下一行之前打印出结果,依此类推。但是,绝对不应该的是在条件代码或函数中深处的print(或者更糟糕的是,input)调用其主要目的显然不是执行I / O。
虽然可以重构此类脚本以将I / O代码与其余数据处理分开,但通常这实际上会使代码难以阅读,因为它将简单的逐步程序流程分解为更复杂的函数调用序列。基本上,如果您的代码自然地像烹饪食谱一样结构化,仅需简单的顺序步骤,则有时最好保持这种状态,而不必通过不必要的重构使其复杂化。

评论


\ $ \ begingroup \ $
这本书的文字写得好极了,谢谢你的解释:)
\ $ \ endgroup \ $
–FoundABetterName
20年11月18日在18:58

\ $ \ begingroup \ $
我认为虽然模块化和可重用性通常很重要,但是将这些原则强加到7 LOC脚本上是过度设计的。尽管有关.endswith()的建议绝对有效
\ $ \ endgroup \ $
– kangalioo
20-11-19在14:21

\ $ \ begingroup \ $
@kangalioo:此类练习的目的是开发可应用于实际任务的编码技能。可以说,遵循良好的编码风格进行此类练习比平时更为重要。通常,“这是扔掉的原型代码,我永远不会重复使用它”可以作为偷工减料的有效论据。但是,在训练编程技能时,即使您知道自己确实不会,也应该始终像编写代码一样在以后重用和扩展代码。
\ $ \ endgroup \ $
–伊尔马里·卡洛宁(Ilmari Karonen)
20-11-19在14:27



#2 楼

唯一令我困扰的是短缩进:-P(标准为四个空格)
endswith通常更好,因为它避免了切片复制的开销,不需要说明长度,并且也可以工作用于空的测试后缀:
>>> for s in 'o', 'a', '':
        print('foo'.endswith(s),
              'foo'[-len(s):] == s)

True True
False False
True False         <= Note the difference!

这是我的处理方法:
string = input()
if len(string) >= 3:
    string += 'ly' if string.endswith('ing') else 'ing'
print(string)


评论


\ $ \ begingroup \ $
我对此的小疑问是您要覆盖原始字符串。
\ $ \ endgroup \ $
–本
20-11-18在16:42

\ $ \ begingroup \ $
@Ben是的,按照指示进行。
\ $ \ endgroup \ $
–凯利·邦迪(Kelly Bundy)
20-11-18在16:43

\ $ \ begingroup \ $
非常真实;整个印刷工作使我失望。由于您的宝贵意见,我将保留我的原始评论。
\ $ \ endgroup \ $
–本
20 Nov 18'在16:45

\ $ \ begingroup \ $
我也刚刚注意到,我们需要对字符串本身进行更改,谢谢:)
\ $ \ endgroup \ $
–FoundABetterName
20-11-18在17:25

\ $ \ begingroup \ $
@FoundABetterName嗯,我们不能真正更改字符串本身,因为它是不可变的。我想说,我们最能做的就是更改变量(这是我要做的)。当然,如果您真的只是输入和打印,那并不重要。
\ $ \ endgroup \ $
–凯利·邦迪(Kelly Bundy)
20 Nov 18 '17:36

#3 楼

我认为代码看起来不错,除非您可以使用库函数来检查它的结尾-以“ ing”:
string = input()
if len(string) < 3:
  print(string)
elif string.endswith('ing'):
  print(string + 'ly')
else:
  print(string + 'ing')


#4 楼

没有很多其他方法可以执行此操作。
可以将if-elif-else语句替换为if-elif-else表达式。是可读性更高还是更少?
您可以使用if语句选择适当的后缀,并在末尾仅放在print上。
print语句中,可以使用f字符串而不是字符串串联
string = input()
suffix = '' if len(string) < 3 else 'ly' if string.endswith('ing') else 'ing'
print(f"{string}{suffix}")


评论


\ $ \ begingroup \ $
我发现这个if-elif-else表达式的时间太长,无法轻易浏览。 @Heap Overflow的答案,使用if len> = 3语句控制字符串+ = if-else表达式,对我来说似乎是一个很好的平衡。并将逻辑与I / O分离。
\ $ \ endgroup \ $
– Peter Cordes
20-11-18在20:50



\ $ \ begingroup \ $
@PeterCordes,我同意。正如我在答案中暗示的那样,if表达式可能不那么可读。
\ $ \ endgroup \ $
– RootTwo
20-11-19在0:07