CS50 Introduction To Python Programming


The CS50's Introduction to Programming with Python is a course offered by Harvard University, focusing on teaching Python programming.
This course, led by Harvard Professor David J. Malan, is designed for students with or without prior programming experience who want to learn Python specifically.
It covers topics such as functions, variables, conditionals, loops, exceptions, libraries, unit tests, file I/O, regular expressions, and object-oriented programming.
The course provides hands-on practice opportunities with exercises inspired by real-world programming problems. It can be taken independently or as a precursor to the more general CS50x course,
which covers computer science and programming with C, Python, SQL, and JavaScript.
The CS50's Introduction to Programming with Python course is available online through platforms like Harvard Online, edX, and YouTube, offering a comprehensive introduction to Python programming


Problem Set 6 Solutions

Lines of Code

One way to measure the complexity of a program is to count its number of lines of code (LOC), excluding blank lines and comments. For instance, a program like # Say hello

 
        name = input("What's your name? ")
        print(f"hello, {name}")
        has just two lines of code, not four, since its first line is a comment, and its second line is blank (i.e., just whitespace). 
        That's not that many, so odds are the program isn't that complex. Of course, just because a program (or even function) has more lines of code than another doesn't necessarily mean it's more complex. For instance, a function like
def is_even(n): if n % 2 == 0: return True else: return False
isn't really twice as complex as a function like
 
        def is_even(n):
            return n % 2 == 0
even though the former has (more than) twice as many lines of code. In fact, the former might arguably be simpler if it's easier to read! So lines of code should be taken with a grain of salt. Even so, in a file called lines.py, implement a program that expects exactly one command-line argument, the name (or path) of a Python file, and outputs the number of lines of code in that file, excluding comments and blank lines. If the user does not specify exactly one command-line argument, or if the specified file's name does not end in .py, or if the specified file does not exist, the program should instead exit via sys.exit. Assume that any line that starts with #, optionally preceded by whitespace, is a comment. (A docstring should not be considered a comment.) Assume that any line that only contains whitespace is blank.



Source Code

            import sys

            lst = []
            
            if len(sys.argv) == 1:
                sys.exit("Too few command-line arguments")
            elif len(sys.argv) > 2:
                sys.exit("Too many command-line arguments")
            elif not sys.argv[1].endswith(".py"):
                sys.exit("Not a Python file")
            else:
                try:
                    with open(f"{sys.argv[1]}") as file:
                        for line in file:
                            if not line.isspace() and not line.strip().startswith("#"):
                                lst.append(line)
                except FileNotFoundError:
                    sys.exit("File does not exist")
                else:
                    print(len(lst))

Pizza Py

Perhaps the most popular place for pizza in Harvard Square is Pinocchio's Pizza & Subs, aka Noch's, known for its Sicilian pizza, which is “a deep-dish or thick-crust pizza.” Students tend to buy pizza by the slice, but Pinocchio's also has whole pizzas on its menu too, per this CSV file of Sicilian pizzas, sicilian.csv, below:
Sicilian Pizza,Small,Large
Cheese,$25.50,$39.95
1 item,$27.50,$41.95
2 items,$29.50,$43.95
3 items,$31.50,$45.95
Special,$33.50,$47.95
See regular.csv for a CSV file of regular pizzas as well. Of course, a CSV file isn't the most customer-friendly format to look at. Prettier might be a table, formatted as ASCII art, like this one:

            +------------------+---------+---------+
            | Sicilian Pizza   | Small   | Large   |
            +==================+=========+=========+
            | Cheese           | $25.50  | $39.95  |
            +------------------+---------+---------+
            | 1 item           | $27.50  | $41.95  |
            +------------------+---------+---------+
            | 2 items          | $29.50  | $43.95  |
            +------------------+---------+---------+
            | 3 items          | $31.50  | $45.95  |
            +------------------+---------+---------+
            | Special          | $33.50  | $47.95  |
            +------------------+---------+---------+
In a file called pizza.py, implement a program that expects exactly one command-line argument, the name (or path) of a CSV file in Pinocchio's format, and outputs a table formatted as ASCII art using tabulate, a package on PyPI at pypi.org/project/tabulate.
Format the table using the library's grid format. If the user does not specify exactly one command-line argument, or if the specified file's name does not end in .csv, or if the specified file does not exist, the program should instead exit via sys.exit.


Source Code

        import csv
        import sys
        from tabulate import tabulate


        def main():
            if len(sys.argv) < 2:
                sys.exit("Too few command-line arguments")
            elif len(sys.argv) > 2:
                sys.exit("Too many command-line arguments")
            else:
                if sys.argv[1][-4:] != ".csv":
                    sys.exit("Not a CSV file")
                else:
                    print(tabulize(sys.argv[1]))


        def tabulize(file):
            try:
                with open(file) as f:
                    reader = csv.reader(f)
                    table = tabulate(reader, headers="firstrow", tablefmt="grid")
                    return table
            except FileNotFoundError:
                sys.exit("File does not exist")


        if __name__ == "__main__":
            main()
            

Note: import regular.csv and sicilian.csv


Scourgify

In a file called scourgify.py, implement a program that:
Expects the user to provide two command-line arguments:
the name of an existing CSV file to read as input, whose columns are assumed to be, in order, name and house, and
the name of a new CSV to write as output, whose columns should be, in order, first, last, and house.
Converts that input to that output, splitting each name into a first name and last name. Assume that each student will have both a first name and last name.
If the user does not provide exactly two command-line arguments, or if the first cannot be read, the program should exit via sys.exit with an error message.


Source Code

            import sys
            import csv
            
            def main():
                check_file()
                students_data = ret_data()
                format_data(students_data)
            
            def check_file():
                if len(sys.argv) == 3:
                    return True
                elif len(sys.argv) < 3:
                    sys.exit('Too few command-line arguments')
                elif len(sys.argv) > 3:
                    sys.exit('Too mant command-line arguments')
            
            def ret_data():
                data_old = sys.argv[1]
                students = []
            
                try:
                    with open(data_old) as file:
                        data = csv.DictReader(file)
                        for student in data:
                            students.append({'full_name': student['name'],'house': student['house']})
                        return students
                except FileNotFoundError:
                    sys.exit('File does not exist')
            
            def format_data(unparsed_data):
                data_new = sys.argv[2]
            
                with open(data_new,'w') as file:
                    writer = csv.DictWriter(file,fieldnames = ['first','last','house'])
                    writer.writeheader()
                    for student in unparsed_data:
                        last_name,first_name = student['full_name'].split(',')
                        student_parsed = {'first':first_name.lstrip(),'last':last_name,'house':student['house']}
                        writer.writerow(student_parsed)
            
            
            if __name__ == "__main__":
                main()

CS50 P-Shirt

In a file called shirt.py, implement a program that expects exactly two command-line arguments:
in sys.argv[1], the name (or path) of a JPEG or PNG to read (i.e., open) as input
in sys.argv[2], the name (or path) of a JPEG or PNG to write (i.e., save) as output
The program should then overlay shirt.png (which has a transparent background) on the input after resizing and cropping the input to be the same size, saving the result as its output.

Source Code

        from PIL import Image, ImageOps
        import sys
        import os


        def main():
            if len(sys.argv) < 3:
                sys.exit("Too few command-line arguments")
            elif len(sys.argv) > 3:
                sys.exit("Too many command-line arguments")
            else:
                format = [".jpg", ".jpeg", ".png"]
                inp = os.path.splitext(sys.argv[1])
                outp = os.path.splitext(sys.argv[2])
                if outp[1].lower() not in format:
                    sys.exit("Invalid output")
                elif inp[1].lower() != outp[1].lower():
                    sys.exit("Input and output have different extensions")
                else:
                    edit_photo(sys.argv[1], sys.argv[2])


        def edit_photo(input, output):
            try:
                shirt = Image.open("shirt.png")
                with Image.open(input) as input:
                    input_cropped = ImageOps.fit(input, shirt.size)
                    input_cropped.paste(shirt, mask = shirt)
                    input_cropped.save(output)
            except FileNotFoundError:
                sys.exit(f"Input does not exist")


        if __name__ == "__main__":
            main()