0

I have something like this, hardcoded:

class csv_row:
  def __init__(self, name, time, more_stuff):
    self.name = name
    self.time = time
    self.more_stuff = more_stuff

this class is the representation of a csv row. What I want to do is make this more generic, and grab the head of the csv file and use it to initialize it in the same way as this list. Something like so:

class csv_row:
  def __init__(self, list_of_attrs):
    for column in list_of_attrs:
      # initialize a self.variable with the same name as the column 

for example, the header for a csv is [name, time, something]. The __init__, when passed that, will initialize with:

self.name = name
self.time = time
self.something = something

How can this be done?

corvid
  • 10,733
  • 11
  • 61
  • 130
  • Looking at the bigger picture, if you are reading CSV from a file, csv.DictReader can help in that regard. – Hai Vu Apr 24 '14 at 17:31

5 Answers5

2

Python allows dynamic manipulation of class instance using dict magic field.

def __init__(self, list_of_attrs):
    for column in list_of_attrs:
       self.__dict__[column] = None

Also, you might want to provide actual values, e.g.:

def __init__(self, **kwargs):
    for name, value in kwargs.items():
       self.__dict__[name] = value

Pythonic way would be to use kwargs, which stands for KeyWord ARGumentS. kwargs is just a convention, actual meaning is given by **, so you can name it as you wish.

Community
  • 1
  • 1
J0HN
  • 26,063
  • 5
  • 54
  • 85
2

Here is an idea: use the keywords:

class csv_row:
    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)


c = csv_row(id=501, alias="john", shell="bash")
print c.id    # 501
print c.alias # john
print c.shell # bash
Hai Vu
  • 37,849
  • 11
  • 66
  • 93
2

There already exist several facilities for dealing with something like this. There is no need to reinvent the wheel:

namedtuple

from collections import namedtuple
headers = ["one", "two", "three"]

CustomRow = namedtuple("CustomRow", headers)

a_row = CustomRow(1, 2, 3)
a_row.one == 1 # True
a_row.two == 2 # True
a_row.three == 3 # True

csv.DictWriter and csv.DictReader

import csv

with open("my_file.csv", "rb") as f:
    reader = csv.DictReader(f, ["one", "two", "three"])
    for line in reader:
        print line["one"] # prints the 1st column
        print line["two"] # etc.
        print line["three"] # etc., etc.
Joel Cornett
  • 24,192
  • 9
  • 66
  • 88
  • alas, I need to set the `__eq__` and `__hash__` functions. Is this possible with namedtuples? – corvid Apr 24 '14 at 17:28
  • 1
    @Crow: If you want those methods to work based on attribute equality, that's already how it works. If not, you can subclass the class created by `namedtuple` to add the methods. Note that the default `__eq__` will make these objects compare equal with ordinary tuples with the same items. – user2357112 Apr 24 '14 at 17:29
2

I realize that it's not exactly what you're asking for in the original comment, but have you thought of using csv.DictReader/csv.DictWriter [1]/[2]? This combined with the other answers might help when creating a clean, robust solution.

  1. https://docs.python.org/2/library/csv.html
  2. https://docs.python.org/3/library/csv.html
0

Not exactly what you're asking, but just in case anybody finds this helpful.

If you know the attributes in advance, and simply want to avoid the duplication in the assignment to self, you can define the argument list once in the __init__ signature, as usual, and then iterate over locals() and assign to __dict__:

class csv_row(object):
  def __init__(self, name, time, more_stuff):
    for k,v in locals().items():
      if k != 'self':
         self.__dict__[k] = v
shx2
  • 61,779
  • 13
  • 130
  • 153