mmarton
mmarton

Reputation: 49

What is the pythonic way of initializing class instances with data from a file?

I am new to OOP and Python in general and having the following problem: Suppose I have a file containing the data line by line that I want to use to create class instances. What is the most pythonic way of processing the file, initializing instances and storing them? I have 3 alternative methods: CASE-1: class method that processes the whole file line by line, creating instances and storing them in a list CASE-2: function outside of the class doing the same as CASE-1 CASE-3: function outside of the class processing each line individually and a for loop in main e.g:

    class TestClass:
    all_instances_1 = []

    def __init__(self, var1, var2):
        self.var1 = var1
        self.var2 = var2

#CASE-1
    @classmethod
    def from_file(cls, input_file):
        my_file = open(input_file, "r")
        for line in my_file:
            var1, var2 = line.split(',')
            new_instance = TestClass(var1, var2])
            cls.all_instances_1.append(new_instance)
        my_file.close()

#CASE-2
def outside_class_init(input_file):
    all_instances_2 = []
    my_file = open(input_file, "r")
    for line in my_file:
        var1, var2 = line.split(',')
        new_instance = TestClass(var1, var2])
        all_instances_2.append(new_instance)
    my_file.close()
    return all_instances_2

#CASE-3
def one_by_one(my_str):
    var1, var2 = line.split(',')
    new_instance = TestClass(var1, var2])
    return new_instance

def main():
    filename = "$FILE"
    TestClass.from_file(filename)
    instances_list2 = outside_class_init(thefile)

    all_instances_3 = []
    my_file = open(thefile, "r")
    for line in my_file:
        all_instances_3.append(one_by_one(line))
    my_file.close()


if __name__ == '__main__':
    main()

If none of them is truly a good approach how would you handle it? Thank you in advance

Upvotes: 1

Views: 627

Answers (1)

chepner
chepner

Reputation: 531175

TestClass is combining two different abstractions: a representation of a single pair of values, and a collection of those pairs. There should be two separate classes. Here's an example:

class Pair:
    def __init__(self, var1, var2):
        self.var1 = var1
        self.var2 = var2

    @classmethod
    def from_string(cls, s):
        v1, v2 = s.split(",")
        return cls(v1, v2)


class LotsaPairs:
    def __init__(self, pairs):
        self.pairs = pairs

    @classmethod
    def from_file(cls, f):
        return cls([Pair.from_string(line.strip()) for line in f])


def main():
    filename = "$FILE"
    with open(filename) as f:
        all_instances = LotsaPairs.from_file(f)

A few observations:

  1. __init__ in both classes is "dumb"; all it does is initialize attributes with a given argument.

  2. The class methods take care of "extracting" data from a specific source to compute the necessary value for __init__.

  3. LotsaPairs.from_file takes a file-like object, not a file name. Let the caller provide that object, to make testing either. (You could pass an io.StringIO instance instead, for example.)

  4. LotsaPairs is a glorified wrapper around a list. You could dispense with it and replace it with a single function:

    def get_pairs(f):
        return [Pairs.from_string(line.strip()) for line in f]
    

Upvotes: 1

Related Questions