I am struggling to understand classes/objects and using a csv with them.
I have a CSV with 26 rows, 1 being the header, the other containing rows of info. Small example below
id,food,food_print,cal1,cal2,expi1999,expi2000,expi2001
1,bun,bun_bun,45.3434,199.32323,23.3333,45.4444,33.33333
2,burger,burger_bun,45.342343,200.34243,34.3333,0,9
3,pickle,pickle_seed,67.345454,34.3434,34,56,33
4,chicken,chicken_egg,44.34343,43.343343,43,434,34343
I have my class as follows:
class City(object):
def __init__(self, food = 'n/a', foodprint = 'n/a', cal1 = -999, cal2 = -999,
expi1999 = -999, expi2000 = -999, expi2001 = -999)
self.food = food
self.foodprint = foodprint
self.cal1 = cal1
self.cal2 = cal2
self.expi1999 = expi1999
self.expi2000 = expi2000
self.expi2001 = expi2001
meals = []
foodfile = open('Food.csv', 'rt')
headers = foodfile.readline().strip().split(',')
headers = headers.split(',')
for line in foodfile:
foodfields = foodfile.readline().strip().split(',')
How do I write in the rows from my food csv into an object to be referenced in the class?
CodePudding user response:
Assuming all colums are filled in every row: try:
for line in foodfile:
foodfields = foodfile.readline().strip().split(',')
meals.append(City(foodfields[1],foodfields[2],foodfields[3],foodfields[4],foodfields[5],foodfields[6],foodfields[7]))
CodePudding user response:
true tone to avoid dynamic classes.
Because your code become unpredictable in future changes. Use dataclass instead.
Also this behaviour let you refer to this "data model" as annotation during data exchange with any callable.
from dataclasses import dataclass
@dataclass
class City:
food: str = 'n/a'
foodprint: str = 'n/a'
cal1: int = -999
cal2: int = -999
expi1999: int = -999
expi2000: int = -999
expi2001: int = -999
But if you want to now how to do it - setattr
function exists in global namespace.
class DynamicContainer:
@classmethod
def create(cls, headers: list[str], data: list[str]):
obj = cls()
for header, value in zip(headers, data):
setattr(obj, header, value)
return obj
headers = 'id,food,food_print,cal1,cal2,expi1999,expi2000,expi2001'.split(',')
data = '1,bun,bun_bun,45.3434,199.32323,23.3333,45.4444,33.33333'.split(',')
cont = DynamicContainer.create(headers, data)
print(cont.id, cont.cal1, cont.expi2000)
CodePudding user response:
Here is how I would do it:
import csv
class City(object):
def __init__(self, food=None, foodprint=None, cal1=None, cal2=None, expi1999=None, expi2000=None, expi2001=None):
self.food = food
self.foodprint = foodprint
self.cal1 = float(cal1)
self.cal2 = float(cal2)
self.expi1999 = float(expi1999)
self.expi2000 = float(expi2000)
self.expi2001 = float(expi2001)
def __repr__(self):
return (
f"{self.__class__.__name__}("
f"food={self.food!r}"
f", "
f"food_print={self.foodprint!r}"
f", "
f"cal1={self.cal1!r}"
f", "
f"cal2={self.cal2!r}"
f", "
f"expi1999={self.expi1999!r}"
f", "
f"expi2000={self.expi2000!r}"
f", "
f"expi2001={self.expi2001!r}"
f")"
)
with open("Food.csv") as stream:
next(stream) # Skip the header row
reader = csv.reader(stream)
meals = [City(*row[1:]) for row in reader]
Here is what rows
looks like:
[City(food='bun', food_print='bun_bun', cal1=45.3434, cal2=199.32323, expi1999=23.3333, expi2000=45.4444, expi2001=33.33333),
City(food='burger', food_print='burger_bun', cal1=45.342343, cal2=200.34243, expi1999=34.3333, expi2000=0.0, expi2001=9.0),
City(food='pickle', food_print='pickle_seed', cal1=67.345454, cal2=34.3434, expi1999=34.0, expi2000=56.0, expi2001=33.0),
City(food='chicken', food_print='chicken_egg', cal1=44.34343, cal2=43.343343, expi1999=43.0, expi2000=434.0, expi2001=34343.0)]
Notes
- The
repr
function is to show the contents of the class object, you can ignore it - I use the
csv
module to ease parsing the CSV data - I also converted the values into floating point numbers.