I gave problem #22 on Project Euler a try in Python3.Other than suggested in the instructions, I used requests to fetch the file to work on.The code below does produce the correct result but takes no measures to catch exceptions if something breaks, e.g. when the download is not successful.I managed to split a string, in which names in double quotes are separated by a comma and turn it to a list, but I'm wondering if there isn't any better or more pythonic way.As far as the summation of int in a list is concerned, I'm aware that reduce from functools might be an alternative, but for my first contribution here, I decided to keep it (more) simple.#!/usr/bin/env python3# -*- coding: utf-8 -*-'''Project Euler - Problem 22 - Name scoresURL: https://projecteuler.net/problem=22Resource (names.txt): https://projecteuler.net/project/resources/p022_names.txtInstructions: Using names.txt (right click and 'Save Link/Target As...'), a 46K text file containing over five-thousand first names, begin by sorting it into alphabetical order. Then working out the alphabetical value for each name, multiply this value by its alphabetical position in the list to obtain a name score.For example, when the list is sorted into alphabetical order, COLIN, which is worth 3 + 15 + 12 + 9 + 14 = 53, is the 938th name in the list. So, COLIN would obtain a score of 938  53 = 49714.Assignment: What is the total of all the name scores in the file?'''import sysimport requestsURL = 'https://projecteuler.net/project/resources/p022_names.txt'def fetch_names(url):    '''Get a text file with names and return a sorted list    Parameters    ----------    url : str          url of a text file at Project Euler.    Returns    -------        alphabetically sorted list of first names    '''    r = requests.get(url)    # r.text is a string with names    # the names are in capital letters, enclosed in double quotes    # and separated by commas    names = [name.strip('') for name in r.text.split(',')]    return sorted(names)def calculate_name_score(pos, name):    '''     Calculate the name sore of a name at a specific position in a list    Parameters    ----------    pos : int         zero-based index of a name in a list    name : str        name in CAPS    Returns    -------    name_score : int    '''    # letter scores: 'A' = 1, 'B' = 2, etc.!    ch_scores = [ord(ch) - 64 for ch in name]    score = sum(ch_scores)    # Project Euler: index of the names starts with 1!!    name_score = score * (pos + 1)    return name_scoredef calculate_total_score(names):    '''Return a name score for a list of names    Parameters    ----------         names : list (of strings)    Returns    -------        total_score : int    '''    scores = [calculate_name_score(pos, name) for (pos, name) in enumerate(names)]    total_score = sum(scores)    return total_score    def main():    names  = fetch_names(URL)    result = calculate_total_score(names)    print(result)if __name__ == '__main__':    sys.exit(main())I'm grateful for any improvement, regardless whether it concerns the structuring of code, naming, documentation, or anything you can think of.
Project Euler #22
python;programming challenge;python 3.x
Code looks pretty good. Nicely commented, well-structured, seems to do what its supposed to do. Just a few small suggestions from me:Dont use magic numbers.  In this line, its not immediately obvious what 64 means:ch_scores = [ord(ch) - 64 for ch in name]If I think a little, and find that ord('A') = 65, I can work out whats happening here. But it would be even clearer to write:ch_scores = [ord(char) - ord('A') + 1 for char in name]Cache the results from ord()? Youll be making a call to ord() at least once for every letter in every name; twice if you adopt my strategy. It might be better to precompute the scores in a global dictionary, e.g.:import stringCHAR_SCORES = {char: ord(char) - ord('A') + 1               for char in string.ascii_uppercase}and then just look up scores in this dictionary. It probably wont make much of a difference for this problem, but its something to bear in mind.Use generator comprehensions unless you need a list. A generator comprehension is like a list comprehension, but instead of precomputing all the elements upfront and storing them in a list, it computes-and-yields one element at a time. That can have big savings in memory.Both of your list comprehensions get passed straight to sum(), which can just get one element at a time  it isnt jumping around the list.The change is minimal  just replace the square brackets of a list comprehension with parentheses instead, i.e. scores = (calculate_name_score(pos, name) for (pos, name) in enumerate(names))