How to iterate class objects with arguments - python

I'm trying to extract info from a csv file. Each line in the file contains several values for arguments that are used in a class. I want to take these values and give them to an object, but it needs to parse each line and create a new object with these argument values.
I would need each object to have a different name, e.g.: obj1 obj2 etc.
Here's how it would operate without iteration (only one object).
with open("file.csv") as csvfile:
file = csv.DictReader(csvfile)
for line in file:
name = line["name"]
cost = line["cost"]
#one object manually entered
obj = Class(name,cost)
csvfile.close()

You can create dictionary of objects and get those objects by corresponding key in dictionary.
obj_dict = {}
with open("file.csv") as csvfile:
file = csv.DictReader(csvfile)
for idx, line in enumarate(file):
name = line["name"]
cost = line["cost"]
#one object manually entered
obj = Class(name,cost)
obj_dict['obj{}'.format(idx+1)] = obj
csvfile.close()
obj_dict['obj1'] # returns first object of type Class
Also:
Another way you can create variable with your name programatically , although it is not the recommended way for instantiating variables in code (for example your IDE might show error because it will think variable is not defined):
_g = globals()
with open("file.csv") as csvfile:
file = csv.DictReader(csvfile)
for idx, line in enumarate(file):
name = line["name"]
cost = line["cost"]
#one object manually entered
obj = Class(name,cost)
_g['obj{}'.format(idx+1)] = obj
csvfile.close()

Use a list, that's what they are for:
with open("file.csv") as csvfile:
reader = csv.DictReader(csvfile)
objects = [Class(row["name"], row["cost"]) for row in reader]
# you don't need to close the file when using `with`
print(objects[0])
print(objects[1])
# etc

Related

How to call a single function multiple times with different parameters in python?

list = ["a","b","c"]
def type(count):
res2=list[count]
for row1 in csv_reader1: # Values imported from CSV
res2=list[count]
print(res2)
res1 = str(row1)[1:-1]
res3 = str(res1)[1:-1]
print(res3)
type(0)
type(1)
type(2)
I want to call this type function, type(0) is being called, but then it exits and type(1) and type(2) are not being called. I've even tried with for loop
for i in range(0,2):
type(i)
i=i+1
Even this For doesn't work, it just calls type(0) and exits.
I've defined a list and I'm trying to iterate list for each value of imported from CSV.
Kind of for-each in powershell - for-each list( print res2( = list) and print(each value in CSV) ) - This is what I'm trying to achieve. I'm new to Python. Any help would be greatly appreciated. Thanks in advance.
I assume you are creating a CSV reader something like this:
import csv
with open("myfile.csv") as f:
csvreader1 = csv.reader(f)
This reader object can only be read once and is then used up. That's why your function doesn't do anything the 2nd and 3rd times. To be able to reuse the content, use list to read the whole file into memory.
with open("myfile.csv") as f:
csv_content = list(csv.reader(f))
Alternatively, rewrite your function so that it reads the CSV each time.
letters = ["a","b","c"]
def print_data(i, filename):
print(letters[i])
with open(filename) as f:
for row in csv.reader(f): # Values imported from CSV
print(str(row)[2:-2])
print_data(0, "myfile.csv")
list = ["a","b","c"]
file = open("C:/Users/data.csv")
csv_reader = csv. reader(file)
def type(count):
res2=list[count]
for row1 in csv_reader: # Values imported from CSV
res2=list[count]
print(res2)
res1 = str(row1)[1:-1]
res3 = str(res1)[1:-1]
print(res3)
for i in range(0,2):
type(i)

How to duplicate a python DictReader object?

I'm currently trying to modify a DictReader object to strip all the spaces for every cell in the csv. I have this function:
def read_the_csv(input_file):
csv_reader = csv.DictReader(input_file)
for row in csv_reader:
for key, value in row.items():
value.strip()
return csv_reader
However, the issue with this function is that the reader that is returned has already been iterated through, so I can't re-iterate through it (as I would be able to if I just called csv.DictReader(input_file). I want to be able to create a new object that is exactly like the DictReader (i.e., has the fieldnames attribute too), but with all fields stripped of white space. Any tips on how I can accomplish this?
Two things: firstly, the reader is a lazy iterator object which is exhausted after one full run (meaning it will be empty once you return it at the end of your function!), so you have to either collect the modified rows in a list and return that list in the end or make the function a generator producing the modified rows. Secondly, str.strip() does not modify strings in-place (strings are immutable), but returns a new stripped string, so you have to rebind that new value to the old key:
def read_the_csv(input_file):
csv_reader = csv.DictReader(input_file)
for row in csv_reader:
for key, value in row.items():
row[key] = value.strip() # reassign
yield row
Now you can use that generator function like you did the DictReader:
reader = read_the_csv(input_file)
for row in reader:
# process data which is already stripped
I prefer using inheritance, make a subclass of DictReader as follows:
from csv import DictReader
from collections import OrderedDict
class MyDictReader(DictReader):
def __next__(self):
return OrderedDict({k: v.strip()
for k, v in super().__next__().items()})
Usage, just as DictReader:
with open('../data/risk_level_model_5.csv') as input_file:
for row in MyDictReader(input_file):
print(row)

Retrieving attributes of objects stored in dictionary

class Customer:
def __init__(self,custid,name,addr,city,state,zipcode):
self.custid=custid
self.name=name
self.addr=addr
self.city=city
self.state=state
self.zipcode=zipcode
self.memberLevel=BasicMember()
self.monthlySpending =0
Well i am able to read a file and then split it such that in dictionary key is my customerid and value is the customer object. But i can't retrieve the attributes for each object stored in my dictionary. How to get each objects attributes from dictionary.
for line in open('customers.dat','r'):
item=line.rstrip(',')
intput =line.split(',')
cc=Customer.Customer(*intput)
s2=item.split(',',1)[0]
d[s2]=[cc]
sample data of customer is :
619738273,Admiral Ackbar,383 NeiMoidian Road,Utapau,MA,01720
118077058,Padme Amidala,846 Amani Road,D'Qar,MA,01508
360513913,Wedge Antilles,700 NeiMoidian Road,D'Qar,MA,01508
while my output after storing each object in dictionary is :
{'739118188': [<Customer.Customer object at 0x005FF8B0>],
'578148567': [<Customer.Customer object at 0x005FF9B0>]}
So how to get attributes for the object stored in the dictionary.
I'm not sure why you wrapped each one in a list, but simply access them as normal:
>>> d['619738273'][0].name
'Admiral Ackbar'
I'd recommend not wrapping each one in a list:
d[s2] = cc
Then you don't need the [0]:
>>> d['619738273'].name
'Admiral Ackbar'
You can also streamline the parsing step:
with open('customers.dat') as f:
for line in f:
k,*data = line.split(',')
d[k] = Customer.Customer(k, *data)
Although it'd be better to use csv, since it looks like you're working with a CSV file:
import csv
with open('customers.dat') as f:
reader = csv.reader(f)
for k,*data in reader:
d[k] = Customer.Customer(k, *data)

Dynamically naming tuples for redis

I have a csv file in which each line contains a person's ID # and then a bunch of attributes. I want to be able to create a tuple for each person that contains all their attributes and then name the tuple some variation of their ID #.
All these tuples will then be added to a set in redis for storage.
I can't seem to figure out how to create a tuple that is named after the persons ID#.
I know its not best practice to dynamically name variables, but I would rather not put all the tuples in a list or set to then put into a redis set (which is a must); it just seems inefficient and cumbersome.
This is the code I have now:
with open('personlist.csv','rb') as f:
for line in f:
row = line.split(',')
personID = row[0]
attrb1 = row[1]
attrb2 = row[2]
attrb3 = row[3]
# Need to name tuple here and define as (attrb1, attrb2, attrb3)
r.lpush('allpersonslist',tuple)
This example needs additional code to function. I'm assuming you are using a redis API such as redis-py. The variable r is an open connection to redis.
import pickle
with open('personlist.csv', 'rb') as f:
for line in f:
row = line.split(',')
personID = row[0]
attrb1 = row[1]
attrb2 = row[2]
attrb3 = row[3]
#put the attributes in a tuple
tuple = (attrb1, attrb2, attrb3)
#serialize the tuple before adding it to the set
r.set("person/%d" %personID,pickle.dumps(tuple,-1))
def getPerson(Id):
return pickle.loads(r.get("person/%d" %Id))
You can call getPerson(5) to return the tuple associated with a person of ID 5.
If each person have max N attribute, there is language-independent solution based on hash. Here list 3 commands to save/read/delete values for a person.
HMSET 'allpersonshash' personID:0 personID:1 ......
HMGET 'allpersonshash' personID:0 personID:1 personID:2 ... personID:N
HDEL 'allpersonshash' personID:0 personID:1 personID:2 ... personID:N
A fairly general way to do it would be to use sorted sets with json blobs, eg:
ZADD userid, '{field1:value1,field2:value2}'

Set a list name in a class, using a function

I have a class to import data from a CSV file, and a function that takes the filename and a name for the output list. I want to set the name of the self.data_name to be self.info using the setattr() function. How can I do this?
import csv
class import_data:
def import_csv(self, filename_csv, data_name):
setattr(self,data_name,0)
datafile = open(filename_csv, 'r')
datareader = csv.reader(datafile)
self.data_name = []
for row in datareader:
self.data_name.append(row)
print("finished importing data")
b = import_data()
b.import_csv('info.csv', 'info')
print(b.info)
This does not work because b.data_name is not b.info. This prints 0 instead of the imported CSV file.
Try this:
class import_data:
def import_csv(self, filename_csv, data_name):
with open(filename_csv, 'r') as f:
setattr(self, data_name, list(csv.reader(f)))
print("finished importing data")
You're going to have to replace all usages of self.data_name in the import_csv() function with calls to either setattr() or getattr() to be able to use the dynamic name.
Using self.data_name will use the member named data_name, as I suspect you've already realised, and that isn't what you want to do.
For example, try the following:
class import_data:
def import_csv(self, filename_csv, data_name):
#set dynamic named item to default value
#not required if this will happen anyway (in this example it does)
setattr(self,data_name,[])
#preparation activities
datafile = open(filename_csv, 'r')
datareader = csv.reader(datafile)
#do required work using a temporary local variable
temp = []
for row in datareader:
temp.append(row)
#copy the temporary local variable into the dynamically named one
setattr(self, data_name, temp)
#tidy up activities
datafile.close()
print("finished importing data")
Make sure you take a look at eumiro's answer, which takes a better, more compact and more Pythonic approach to your specific problem using with and list(). However, the above should hopefully make it clear how you could be using setattr() in a wider variety of cases.

Categories