Dynamically add CSV header as attribute name of Model class using setattr of python

There is a convenient module in python that loads csv. I tried inserting a DB test record using that. At that time, if each CVS column value is assigned to Model by describing the attribute name as shown below, the source code will be changed due to the change of column value and Model class. We will refactor to eliminate this.

The advantages and usage of setattr may be understood by reflecting, but I think this sample is easier to understand.

The execution environment is assumed to be Python 3.4.3.

import csv

class Model():
    pass

if __name__ == '__main__':
    models = []
    with open('person.csv', 'r') as f:
        reader =  csv.reader(f)
        #Read only one line (header)
        Model._columns = next(reader)
        for row in reader:
            model = Model()
            #Dynamically set attributes to model
            for i in range(len(row)):
                model.coulmn1 = row[0]
                model.coulmn2 = row[1]
            models.append(model)

Use setattr to set attributes

I will post the csv file to be used this time first.

id,name,age
1,Taro,19
2,Ziro,18
3,Mikio,40
4,Mineko,38

Python has a built-in function called setattr. Use as setattr (object, attribute name, value). What's good about this is that you can specify the attribute name with a variable. If you set ʻobj.var1, it will search for var1 in obj. If you want var1 to expand as a variable, use setattr ()`.

import csv

class Model():
    pass

if __name__ == '__main__':
    models = []
    with open('person.csv', 'r') as f:
        reader =  csv.reader(f)
        #Read only one line (header)
        columns = next(reader)
        for row in reader:
            model = Model()
            #Dynamically set attributes to model
            for i in range(len(row)):
                setattr(model, columns[i], row[i])
            models.append(model)

    for model in models:
        print(model)

# =>
# <__main__.Model object at 0x1053169b0>
# <__main__.Model object at 0x105316a58>
# <__main__.Model object at 0x105316b00>
# <__main__.Model object at 0x105316ba8>

Maintain the order of the attribute list

Since the attribute of the object is not output as it is, it will be output by default even if dir is not used. In python, a special method called __str__ is executed when printing with a print statement. All you have to do is return the string you want to display with this method.

Those whose prefix is not _ (underscore) are excluded from the display. If you want to define a method for internal processing in Model by this, you can add _ (underscore).

import csv

class Model():
    #Because it is included in the number of attributes when comparing with the length of the element
    #Add underscore
    _columns = []

    def __init__(self):
        if len(self._columns) == 0:
            raise Exeption("Please set the columns of %s" % self)

    def __str__(self):
        attribute_str = ""
        #The prefix is_Exclude
        attributes = list(filter(
                            lambda attribute_name: not(attribute_name[0].startswith("_")),
                            dir(self)))

        if len(attributes) == len(self._columns):
            #Refer to the class variable because self does not have columns
            for attribute in self._columns:
                attribute_str += "%s = %s \n" % (attribute,
                                                 getattr(self, attribute))
        else:
            raise Exception("Incorrect the number of %s.columns" % self)

        return attribute_str

if __name__ == '__main__':
    models = []
    with open('person.csv', 'r') as f:
        reader =  csv.reader(f)
        #Read only one line (header)
        Model._columns = next(reader)
        for row in reader:
            model = Model()
            #Dynamically set attributes to model
            for i in range(len(row)):
                setattr(model, Model._columns[i], row[i])
            models.append(model)

    for model in models:
        print(model)

# =>
# id = 1 
# name = Taro 
# age = 19 
# 
# id = 2 
# name = Ziro 
# age = 18 
# 
# id = 3 
# name = Mikio 
# age = 40 
# 
# id = 4 
# name = Mineko 
# age = 38

Recommended Posts

Dynamically add CSV header as attribute name of Model class using setattr of python
Character encoding when using csv module of python 2.7.3
[Python] Implementation of clustering using a mixed Gaussian model
Add the attribute of the object of the class with the for statement
Sample of getting module name and class name in Python
Name identification using python
[Python3] Format the character string using the variable name as the key.