编写更短、更易读、更符合Python习惯的代码

5
我正在努力编写更短、更Pythonic、更易读的Python代码。我已经有了一个可行的解决方案,可以找到1000位数字中5个连续数字的最大乘积,这是为 Project Euler 的问题8 设计的。(原题链接) 您对这个脚本的 Pythonic 版本有什么建议吗?
numstring = ''
for line in open('8.txt'):
    numstring += line.rstrip()

nums = [int(x) for x in numstring]

best=0
for i in range(len(nums)-4):
    subset = nums[i:i+5]
    product=1
    for x in subset:
        product *= x
    if product>best:
        best=product
        bestsubset=subset

print best
print bestsubset

例如:下面的代码段必须有一个一行代码。我确定这里以前有过相关主题,但我不确定如何描述我下面要做的事情。
numstring = ''
for line in open('8.txt'):
    numstring += line.rstrip()

有什么建议吗?谢谢大家!
5个回答

4

我正在着手写一份完整的答案,但目前先提供一句简短的回答:

numstring = ''.join(x.rstrip() for x in open('8.txt'))

编辑:给你!搜索的一行代码。列表推导式真是太棒了。

from operator import mul
def prod(list):
    return reduce(mul, list)

numstring = ''.join(x.rstrip() for x in open('8.txt'))
nums = [int(x) for x in numstring]
print max(prod(nums[i:i+5]) for i in range(len(nums)-4))

真是太棒了。你觉得使用lambda代替mul怎么样? 即 def prod(list): return reduce(lambda x,y: x*y, list) - dyln
那也很好用。我不知道为什么Python没有将其构建进去 - 这是一个相当普遍的需求(尤其是对于欧拉计划!),而在R中将其作为内置功能确实有所帮助。 - Rob Volgman
1
当Python提供内置的operator.mul时,通常使用它比使用lambda更有效率。对于像这样的东西,效率并不是很重要;您的计算机将在眨眼之间找到答案,因此您可以使用自己喜欢的方式。但是一般来说,在使用reduce()map()等功能时,从operator导入模块并不是坏事。 - steveha

4
from operator import mul

def product(nums):
    return reduce(mul, nums)

nums = [int(c) for c in open('8.txt').read() if c.isdigit()]
result = max((product(nums[i:i+5]) for i in range(len(nums))))

@thebjorn:我故意没有减去4,因为它对结果没有影响。如果我要减去的话,我可能会做类似于range(len(nums) - 5 + 1)这样的事情,甚至在那时命名魔法数字。 - Steven Rumbalski
这里使用了一些相当优雅的技巧。但是你使用max()key的方式意味着你的result被设置为5个数字的序列,而不是它们的乘积。更好的方法是简单地使用result = max(product(nums[i:i+5]) for i in range(len(nums))) - steveha
我必须说,我真的很喜欢创建nums的列表推导式。不用使用.replace()来摆脱行末结束符;只需一次性提取数字并将其转换为整数。优雅。 - steveha
@steveha。我误读了问题。我以为需要实际的序列。我会编辑答案。 - Steven Rumbalski
@steveha。啊,我明白了。原帖保留了实际的序列,但是欧拉计划问题并不需要它。 - Steven Rumbalski
@stevenrumbalski 是的,那个留下来是因为我很好奇。在发布之前我应该把它(即“print bestsubset”)删掉的。 - dyln

1

这是我的解决方案。我试图编写我所知道的最“Pythonic”的代码。

with open('8.txt') as f:
    numstring = f.read().replace('\n', '')

nums = [int(x) for x in numstring]

def sub_lists(lst, length):
    for i in range(len(lst) - (length - 1)):
        yield lst[i:i+length]

def prod(lst):
    p = 1
    for x in lst:
        p *= x
    return p

best = max(prod(lst) for lst in sub_lists(nums, 5))
print(best)

可以说,这是使用reduce的理想情况之一,因此也许prod()应该是:

# from functools import reduce   # uncomment this line for Python 3.x
from operator import mul
def prod(lst):
    return reduce(mul, lst, 1)

如果有理由使用多行代码,我不喜欢尝试编写单行代码。 我真的很喜欢使用with语句,并且我的习惯是将其用于所有I / O操作。 对于这个小问题,您可以只做一个单行,如果您正在使用PyPy或其他类似的东西,当您的小程序执行完并退出时,文件将被关闭。 但是我喜欢使用with的两行代码,所以我写了这个。

我喜欢@Steven Rumbalski的单行代码:

nums = [int(c) for c in open('8.txt').read() if c.isdigit()]

这是我可能会写的方式:
with open("8.txt") as f:
    nums = [int(ch) for ch in f.read() if ch.isdigit()]

再次强调,对于这种短程序而言,当程序退出时您的文件将会关闭,因此您无需担心确保文件被关闭;但我习惯使用with


是的,我认为sub_lists(lst, length)的定义非常合理。使用魔法数字,如len(nums)-4,会让人感到困惑。 - dyln
使用像 prod 这样的定义比使用内置的 operator 中的 mul 明显要慢得多。 - Rob Volgman

0

至于解释最后一部分是什么,首先您需要创建一个名为numstring的空字符串

numstring = ''

然后你循环处理8.txt文件中的每一行文本(或者字符串行):

for line in open('8.txt'):

对于每一行,您需要将line.rstrip()的结果添加到其中。 rstrip会从字符串中“剥离”空格(换行符、空格等):
    numstring += line.rstrip()

假设您有一个名为8.txt的文件,其中包含文本:LineOne \nLyneDeux\t\nLionTree,则最终结果会类似于此内容:
>>>'LineOne' #loop first time
>>>'LineOneLyneDeux' # second time around the bush
>>>'LineOneLyneDeuxLionTree' #final answer, reggie

感谢@TankorSmash的详细解释。我应该在我的问题中表述得更清楚,我的意思是:我不知道如何简洁地描述我正在做的事情,以便搜索过去的主题。 - dyln

0

这是一个完整的解决方案!首先读出数字:

with open("8.txt") as infile:
    number = infile.replace("\n", "")

然后创建一个由5个连续数字的列表列表:

cons_numbers = [list(map(int, number[i:i+5])) for i in range(len(number) - 4)]

然后找到最大值并打印出来:

print(max(reduce(operator.mul, nums) for nums in cons_numbers))

如果您正在使用Python 3.x,则需要将reduce替换为functools.reduce

您可以将 '\n' 替换为 '' - JBernardo
@nightcracker: range(len(number) - 5) 是一个错误。在 '123456789' 上测试它。它会漏掉数字 9 - Steven Rumbalski
我不是 Uncle Timmy,所以我的 Guido 通道可能有误,但我相信首选方法应该是 max(reduce(operator.mul, nums) for nums in cons_numbers),key/lambda 结构相当晦涩。 - thebjorn
我认为你没有测试这段代码。(0) 你需要执行infile.read().reduce而不是infile.reduce。(1) 你需要import operator(也许你只是让读者自己写)。(2) 最终结果是一个由5个整数组成的列表;与其使用max()函数和键值,不如使用生成器表达式来将乘积应用于每个子列表,以获得所需的数字。 - steveha
@steveha和@thebjorn:你们说得对,问题是关于__产品__而不是来源。让我来修正一下。 - orlp
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接