# Writing shorter, readable, pythonic code

I'm trying to produce shorter, more pythonic, readable python. And I have this working solution for Project Euler's problem 8 (find the greatest product of 5 sequential digits in a 1000 digit number).

Suggestions for writing a more pythonic version of this script?

``````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
``````

For example: there's gotta be a one-liner for the below snippet. I'm sure there's a past topic on here but I'm not sure how to describe what I'm doing below.

``````numstring = ''
for line in open('8.txt'):
numstring += line.rstrip()
``````

Any suggestions? thanks guys!

Here is my solution. I tried to write the most "Pythonic" code that I know how to write.

``````with open('8.txt') as f:

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)
``````

Arguably, this is one of the ideal cases to use `reduce` so maybe `prod()` should be:

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

I don't like to try to write one-liners where there is a reason to have more than one line. I really like the `with` statement, and it's my habit to use that for all I/O. For this small problem, you could just do the one-liner, and if you are using PyPy or something the file will get closed when your small program finishes executing and exits. But I like the two-liner using `with` so I wrote that.

I love the one-liner by @Steven Rumbalski:

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

Here's how I would probably write that:

``````with open("8.txt") as f:
nums = [int(ch) for ch in f.read() if ch.isdigit()]
``````

Again, for this kind of short program, your file will be closed when the program exits so you don't really need to worry about making sure the file gets closed; but I like to make a habit of using `with`.

• Yeah I think that the definition of `sub_lists(lst, length)` makes a lot of sense. It was confusing to use the magic number as in `len(nums)-4`. – dyln Jul 27 '12 at 19:55
• Using a definition of prod like that is significantly slower than using the builtin `mul` from `operator`. – Rob Volgman Jul 31 '12 at 20:30

I'm working on a full answer, but for now here's the one liner

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

Edit: Here you go! One liner for the search. List comprehensions are wonderful.

``````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))
``````
• That's really slick. what do you think of using a lambda instead of mul? i.e. `def prod(list): return reduce(lambda x,y: x*y, list)` – dyln Jul 27 '12 at 19:00
• That works well too. I don't know why python didn't build it in - it's a pretty common requirement (even more so for Project Euler!), and it really helped having it as a built-in in R. – Rob Volgman Jul 27 '12 at 19:03
• Where Python offers a built-in like `operator.mul`, it is generally more efficient to use that rather than a `lambda`. For something like this, the efficiency doesn't really matter; your computer will find the answer in the blink of an eye, so you can use what you prefer. But in general, it's not bad to be in the habit of importing from `operator` when you are doing things with `reduce()` or `map()` or whatever. – steveha Jul 27 '12 at 19:09
``````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: I intentionally didn't subtract 4 because it didn't impact the result. If I was going to subtract I would have probably done something like `range(len(nums) - 5 + 1)` and maybe even named the magic number at that point. – Steven Rumbalski Jul 27 '12 at 18:43
• There are some rather elegant tricks being used here. But your use of `max()` with a `key` means that your `result` is set to the sequence of 5 numbers, not to their product. It would be better to simply use `result = max(product(nums[i:i+5]) for i in range(len(nums)))` – steveha Jul 27 '12 at 19:08
• I have to say I really like the list comprehension that creates `nums`. Never mind using `.replace()` to get rid of line endings; just pull only the digits and convert them to integers in one go. Elegant. – steveha Jul 27 '12 at 19:19
• @steveha. I misread the problem. I thought it needed the actual sequence. I'll edit the answer. – Steven Rumbalski Jul 27 '12 at 20:10
• @steveha. Ah. I see. The OP keeps the actual sequence, but the Project Euler problem does not require it. – Steven Rumbalski Jul 27 '12 at 20:12

As far as explaining what that last bit was, first you create an empty `string` called `numstring`:

``````numstring = ''
``````

Then you loop over every line of text (or line of `string`s) in the `txt` file `8.txt`:

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

And so for every line you find, you want to add the result of `line.rstrip()` to it. `rstrip` 'strips' the whitespace (newlines,spaces etc) from the string:

``````    numstring += line.rstrip()
``````

Say you had a file, `8.txt` that contains the text: `LineOne \nLyneDeux\t\nLionTree` you'd get a result that looked something like this in the end:

``````>>>'LineOne' #loop first time
>>>'LineOneLyneDeux' # second time around the bush
``````
• Thanks for the thoughtful explanation @TankorSmash. I should have been clearer in my question, what I meant was: I dont know how to describe what I'm doing here succinctly enough to search for past topics on it. – dyln Jul 28 '12 at 20:29

Here's a full solution! First read out the number:

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

Then create a list of lists with 5 consecutive numbers:

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

Then find the largest and print it:

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

If you're using Python 3.x you need to replace `reduce` with `functools.reduce`.

• you can just replace `'\n'` with `''` – JBernardo Jul 27 '12 at 18:05
• @JBernardo: sure, but that'll split on any whitespace, and `"\n"` makes the intent more clear. – orlp Jul 27 '12 at 18:05
• @JBernardo: ah now I see, yeah that's probably better. – orlp Jul 27 '12 at 18:08
• @nightcracker: `range(len(number) - 5)` is a bug. Test it on '123456789'. It misses the digit `9`. – Steven Rumbalski Jul 27 '12 at 18:23
• map, reduce, and lambda aren't consider Pythonic by Guido ( artima.com/weblogs/viewpost.jsp?thread=98196 ). – thebjorn Jul 27 '12 at 18:37
{"ft_lang_label":"__label__en","ft_lang_prob":0.74872106,"math_prob":0.76657933,"size":798,"snap":"2020-10-2020-16","text_gpt3_token_len":206,"char_repetition_ratio":0.12342569,"word_repetition_ratio":0.09677419,"special_character_ratio":0.26566416,"punctuation_ratio":0.125,"nsfw_num_words":0,"has_unicode_error":false,"math_prob_llama3":0.9781576,"pos_list":[0],"im_url_duplicate_count":[null],"WARC_HEADER":"{\"WARC-Type\":\"response\",\"WARC-Date\":\"2020-02-21T10:19:25Z\",\"WARC-Record-ID\":\"<urn:uuid:29c1d020-c4c1-4776-9ccd-21979768cf9f>\",\"Content-Length\":\"181907\",\"Content-Type\":\"application/http; msgtype=response\",\"WARC-Warcinfo-ID\":\"<urn:uuid:7c0f83fb-13dd-4d7b-bf9d-626d7901c04a>\",\"WARC-Concurrent-To\":\"<urn:uuid:f73902d8-2c74-4fad-acf6-ec05cdd1cf45>\",\"WARC-IP-Address\":\"151.101.129.69\",\"WARC-Target-URI\":\"https://stackoverflow.com/questions/11693115/writing-shorter-readable-pythonic-code\",\"WARC-Payload-Digest\":\"sha1:ETZKLNU5ITL5GS5TGCWHIFIYH7QYVCDE\",\"WARC-Block-Digest\":\"sha1:PC5ZH4WDOGRS2CO7DRF2CXINZME3SMVD\",\"WARC-Identified-Payload-Type\":\"text/html\",\"warc_filename\":\"/cc_download/warc_2020/CC-MAIN-2020-10/CC-MAIN-2020-10_segments_1581875145500.90_warc_CC-MAIN-20200221080411-20200221110411-00418.warc.gz\"}"}