1
\$\begingroup\$

I was messing around with the csv package and I tried to implement a class that returns csv objects whenever I need to open a csv file directly from a url(and occasionally prints out something).I am still very green and I would like to know if there is something I can do to optimize this code.You are going to find the description of every method in each docstring.There are still some functionalities that I want to add, but I would like to know if the design of this very simple class is acceptable and if there is something that I could make better or faster. Thank you for your attention and time.

import csv, urllib.request

class read_CSV_URL:

    def __init__(self, url):
        self.request = urllib.request.urlopen(url)

    def read_csv_from_url(self, delimiter=",", num_of_lines="all", print_lines=False) -> object:
        """
        Read a csv file form a url.
        Three default values:
            1. Delimiter is a standard coma.
            2. Number of lines that are going to be printed
               In case you wanted just the first three lines of
               your file you can just set num_of_lines=3.
            3. In case you want to print the lines set, print_lines=True.

        """
        lines = [l.decode('utf-8') for l in self.request.readlines()]  # decode response
        csv_object = csv.reader(lines, delimiter=delimiter)
        if print_lines:
            if num_of_lines == "all":
                for line in csv_object:
                    print(line)
            else:
                for number in range(num_of_lines):
                    row = next(csv_object)
                    print(row)

        return csv_object

    def read_csv_from_url_as_dict(self, delimiter=",", num_of_lines="all", print_lines=False) -> object:
        """
        Read a csv file form a url as a dictionary.
        Three default values:
            1. Delimiter is a standard coma.
            2. Number of lines that are going to be printed
               In case you wanted just the first three lines of
               your file you can just set num_of_lines=3.
            3. In case you want to print the lines set, print_lines=True.

        """
        lines = [l.decode('utf-8') for l in self.request.readlines()]  # decode response
        csv_object = csv.DictReader(lines, delimiter=delimiter)
        if print_lines:
            if num_of_lines == 'all':
                for dicty in csv_object:
                    print(dicty)
            else:
                for number in range(num_of_lines):
                    print(next(csv_object))

        return csv_object

    def get_key(self, key : str, csv_object):
        """
        Get a single key given a csv_dictionary_object.
        """
        listy = []
        for row in csv_object:
            listy.append(row[key])
        return listy

    def get_keys(self, csv_object, **kwargs) -> list:
        listy = []
        for row in csv_object:
            sub_list = []
            for key in kwargs.values():
                sub_list.append(row[key])
            listy.append(sub_list)
        return listy


csv_1 = read_CSV_URL('http://winterolympicsmedals.com/medals.csv')
print(csv_1.read_csv_from_url_as_dict(print_lines=True))
\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

This is doing too much. You're trying to support reading rows as both a list and a dict; both printing and not; and reading all lines or some lines.

Change the class name to follow PEP 8.

Drop most of your configuration options.

  • Don't print lines as you go. This was debugging code and can be removed in the final version.
  • Either return list or dict items but not both--write two classes if you really need both options.
  • Always read all lines. Let the user directly iterate over the class. As a feature, support streaming access--I assume the reason you're avoiding reading all lines is so you can print faster.
  • Drop get_key and get_keys entirely, make them a static method, or move them out of the class.

A piece of example code, the way I would want to use this as a user:

for row in read_CSV_URL('http://winterolympicsmedals.com/medals.csv'):
​    print(row)
\$\endgroup\$
1
\$\begingroup\$

Building off of what Zachary Vance said: since you haven't shown much usage of this class, I can only assume that some of your features (like supporting partial body retrieval) are not needed; and others should be moved like printing, which really should not be integrated.

Generally, don't use urllib.request when requests exists. A basic implementation of line-buffered, streamed CSV retrieval can look like

from csv import DictReader
from pprint import pprint
from typing import Iterator, Dict

import requests


def read_csv_url(url: str, *args, **kwargs) -> Iterator[Dict[str, str]]:
    with requests.get(url, stream=True) as resp:
        resp.raise_for_status()
        file_like = resp.iter_lines(decode_unicode=True)
        yield from DictReader(f=file_like, *args, **kwargs)


def test() -> None:
    for record in read_csv_url('http://winterolympicsmedals.com/medals.csv'):
        pprint(record)


if __name__ == '__main__':
    test()
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.