Output error using OOP - python

I'm having trouble finding the error in my code. I'm working with the class ColHe which is a collection of objects of type Header, and the main goal is to read a file's header and obtain a list with it's elements.
This is my Header class:
class Header:
def __init__(self, col):
self._day = 'Day:'
self._date = col[1]
self._time = 'Time:'
self._hours = col[3]
self._company = 'Company:'
self._name = 'NSHF'
def __str__(self):
return str(self._day + self._date + ", " + self._time + self._hours
+ ", " + self._company + self._name)
Along with its methods of getters and setters. And this is my ColHe class:
from collections import UserList
from copy import deepcopy
import constants
from Header import Header
class ColHe(UserList):
def __init__(self, file_name):
super().__init__()
in_file=open(file_name)
for i in range(constants.HEADER):
self.append(Header((in_file.readline().strip())))
in_file.close()
def getData(self):
return deepcopy(self)
def __str__(self):
st = ""
for he in self:
st+=str(he)
return st
Where constants.Header equals 6.
I ran the program with:
a=ColHe("file.txt")
print(a.getData())
And got this as an output:
Day:a, Time::, Company:NSHFDay:6, Time:1, Company:NSHFDay:i, Time:e, Company:NSHFDay:4, Time:5, Company:NSHFDay:o, Time:p, Company:NSHFDay:S, Time:F, Company:NSHF
However, the output I'm looking for looks more like this:
["Day:", self._date, "Time:", self._time, "Company:", "NSHF"]
An example of the content of a file would be:
Day:
06:11:2017
Time:
14:55
Company:
NSHF

When you print something in python, you don't print the name you have it stored under, you print its value. IE:
print(self._date) # print whatever is stored in _date
print("self._date") # print the static string 'self._date'
There's no reasonable way to get to your desired output from your demonstrated input. You're misunderstanding core concepts about what values are, where they are stored, and what happens when you reference them.

Use str.format (or f-strings if your code is python 3.6+ only)
def __str__(self):
return 'Day:\n{self._day}\nTime:\n{self._time}\nCompany:\n{self._company}'.format(self=self)

Related

How to use __str__ function to return a string representation of a class

I have been tasked with creating an inventory manager in python, and one of the required is to define the following function within the class:
__str__ - This function returns a string representation of a
class.
The class refers to a file (inventory.txt) which has the following format:
Country,Code,Product,Cost,Quantity
my code thus far is as follows:
# Creating the class:
class Shoes():
# Constructor:
def __init__(self,country,code,product,cost,quantity):
self.country = country
self.code = code
self.product = product
self.cost = cost
self.quantity = quantity
# Methods:
def get_cost():
inventory = open('inventory.txt','r')
inventory_list = inventory.readlines()
code = input("What is the code of the product:")
for line in inventory_list:
split_lines = line.split(",")
if code == split_lines[1]:
print("This product costs R{}".format(split_lines[3]))
inventory.close()
def get_quantity():
inventory = open('inventory.txt','r')
inventory_list = inventory.readlines()
code = input("What is the code of the product:")
for line in inventory_list:
split_lines = line.split(",")
if code == split_lines[1]:
print("There are {} units in inventory".format(split_lines[4]))
inventory.close()
def __str__(self):
pass
I haven't come across the str so I really am not sure how it works, and how to use it. Any advice would be greatly appreciated.
Here is an example:
def __str__(self):
return f'{self.self.country},{self.self.code},{self.self.product}, {self.cost},{self.quantity })'
This way, when you assign values to a class, you can print its string representation
print(new_shoe)
More info here
https://www.pythontutorial.net/python-oop/python-str/

I dont know where I'm going wrong trying to create an object in a subclass through inputs

I've tried many different things so it's a little all over the place, please help
I've been able to make the first class and then in a different file create some objects for it, but for this subclass I need to use user input and I just can't figure it out.
I have made it so the shift input has to be a 1 or 2 for a day or night shift, I just don't have the knowledge for this.
class Employee:
def __init__(self, name, id, dept, title):
self.__name = name
self.__id = id
self.__dept = dept
self.__title = title
def get_name(self):
return self.__name
def get_id(self):
return self.__id
def get_dept(self):
return self.__dept
def get_title(self):
return self.__title
def __str__(self):
result = ""
result += "Name: " + self.get_name() + "\tID Number: " + str(self.get_id()) + \
"\tDepartment: " + self.get_dept() + "\tJob Title:" + self.get_title()
return result
class ShiftEmployee(Employee):
def __init__(self, name, id, dept, title, shift, pay):
Employee.__init__(self, name, id, dept, title)
self.__shift = shift
self.__pay = pay
#classmethod
def inputs(self):
self.__name = input("Enter name: ")
self.__id = input("Enter ID number: ")
self.__dept = input("Enter department: ")
self.__title = input("Enter Jobe title: ")
self.__shift = input("Enter shift: ")
self.__pay = input("Enter hourly pay: ")
#set_shift(self, shift):
#self.__shift = shift
#def set_pay(self, pay):
#self.__pay = pay
def get_shift(self, shift):
if self.__shift == 1:
return "Day"
elif self.__shift == 0:
return "Night"
else:
return "Invalid entry"
def get_pay(self, pay):
return self.__pay
def __str__(self):
result = ""
#result += Employee.__str__(self)
result += "Name: " + self.get_name(ShiftEmployee) + "\tID Number: " + str(self.get_id(ShiftEmployee)) + \
"\tDepartment: " + self.get_dept(ShiftEmployee) + "\tJob Title:" + self.get_title(ShiftEmployee) + \
"\tShift: " + self.get_shift(ShiftEmployee) + "\tHourly Pay: " + str(self.get_pay(ShiftEmployee))
return result
shift_emp = ShiftEmployee
shift_emp.inputs()
print(shift_emp.__str__(ShiftEmployee))
Don't use a classmethod because
A class method is a method that’s shared among all objects.
Though python itself does not force this behavior, your use of self in the inputs definition indicates that you are not doing what you think. the parameter is traditionally named cls in #classmethod-annotated methods, because the object you're referring to inside the body is not an instance of the class, but the class object itself. This means if you have multiple ShiftEmployee objects, they're going to be writing their data to the same variables. This is not what you want to happen.
you are not instantiating a ShiftEmployee object with shift_emp = ShiftEmployee, but rather assigning the class to the variable shift_emp, which is not what you want to do. so if you remove the #classmethod annotation, I think what you want is
shift_emp = ShiftEmployee() # __init__ gets called when you use this constructor invocation
shift_emp.inputs()
print(shift_emp)
Your __str__ methods don't make a lot of sense. You are passing the class object to each getter, which doesn't seem like it's what you'd want to do. The class object defines the class, what you want are the instances of the class. It's an important, if initially confusing distinction. Posting the error you get would help, but here's what I would expect the methods to look like. I'm not using the getters, because this is internal access, but you can use them instead of directly referring to the state variables if you prefer.
# Employee
def __str__(self):
return f"Name: {self.__name} ID Number: {self.__id} Department: {self.__dept} Job Title: {self.__title}"
# ShiftEmployee
def __str__(self):
return super(ShiftEmployee, self).__str__() + f" Shift: {self.__shift} Hourly Pay: {self.__pay}"
So what's going on here? For one thing, we use format strings because they are easier to work with and exactly the thing you wanted. Then we're using the superclass (Employee) to provide the shared functionality, and using the descendent class to enrich with the ShiftEmployee-only data. I skipped the accessor methods because they're redundant when accessing "private" data from inside the class members. Note that this won't quite do what you expect, either, w.r.t. the shift value that gets printed -- it's going to print the int, not "Night" or "Day". This is where your accessor method comes into play, except that your accessor has an extraneous parameter, shift. So you'd have to remove that value.
Please use the following way to initialize the class and printing the class,
shift_emp = ShiftEmployee() # Added Parenthesis
shift_emp.inputs()
print(str(shift_emp)) # Pass class object to inbuilt str() method to get output from __str__() method from class

Listing contents of a list containing class objects not working properly

I have a class task that contains information of a task and a method to expand it as follows:
#define class for tasks
class task:
def __init__(self, name, date, category):
self.name = name
self.date = date
self.category = category
def expand(self): # returns the contents of the task
print(str(self.name) + " is due " + str(self.date))
and a function listTasks() that lists the contents of a standard list that contains objects of the class task as follows:
def listTasks():
i = 1
for task in data:
print("%s. %s" % (i, task.expand()))
i = i+1
The idea is for the listTasks function to display a coherent list that looks like
1. Foo is due tomorrow
2. Foobar is due in 2 days
3. FooFoo is due in five years
etc.. but the actual output looks as follows:
foo is due tomorrow
1. None
foobar is due in 2 days
2. None
Can anyone help me understand why?
your function expand() is just printing the result and not returning it. So inside the loop it is being called and printing the line, but returning None and then the print from the loop becomes i. None.
Change the function to:
def expand(self): # returns the contents of the task
return str(self.name) + " is due " + str(self.date)
To make it more readable I would also use enumerate for the loop, like so:
def listTasks():
for i, task in enumerate(data, 1):
print("%s. %s" % (i, task.expand()))
The main issue as #Tomerikoo pointed out is that you're not returning rather printing the string in your expand method.
However I wanted to point out a couple more possible improvements to your code:
Class names should be CamelCase, and functions should be lower_case_separated_with_underscores according to PEP8.
__repr__ __str__ is a the built-in method to compute the "official" string representation of an object
f-strings make your code more readable
you can use enumerate to iterate through a list with a counter
from datetime import datetime
class Task:
def __init__(self, name, date, category):
self.name = name
self.date = date
self.category = category
def __str__(self):
return f"{self.name} is due {self.date}" # you need to return the information
data = [Task("test", datetime.now(), "something"), Task("test2", datetime.now(), "something")]
def list_tasks():
for i, task in enumerate(data, 1):
print(f"{i}. {task}")
list_tasks()

How to save code doing it with OOP?

I'm writing a program to extract some data from txt files with regular expressions.
I'm new in OOP and want to save reiterative code. I want to retrieve about 15 data in each txt file, so I wrote a Class definition for each data. The patters to match can come in several formats, so I'll need to try several regex patters. By now, I only implements one regex patterns by data, but in future I need to try more in order to match the specific format used in that txt file, I plan to use a list with de patterns for each data.
I've just wrote 3 classes, but I've realized that I'm repeating too much code. So, I believe that I'm doing something wrong.
import re
import os
import glob
import csv
class PropertyNumber(object):
pattern_text = "(?<=FINCA Nº: )\w{3,6}"
regex_pattern = re.compile(pattern_text)
def __init__(self, str):
self.text_to_search = str
self.text_found = ""
def search_p_number(self):
matched_p_number = PropertyNumber.regex_pattern.search(self.text_to_search)
print(matched_p_number)
self.text_found = matched_p_number.group()
return self.text_found
class PropertyCoefficient(object):
pattern_text = "(?<=Participación: )[0-9,]{1,8}"
regex_pattern = re.compile(pattern_text)
def __init__(self, str):
self.text_to_search = str
self.text_found = ""
def search_p_coefficient(self):
matched_p_coefficient = PropertyCoefficient.regex_pattern.search(self.text_to_search)
print(matched_p_coefficient)
self.text_found = matched_p_coefficient.group()
return self.text_found
class PropertyTaxIDNumber(object):
pattern_text = "(?<=Referencia Catastral: )\d{7}[A-Z]{2}\d{4}[A-Z]\d{4}[A-Z]{2}"
regex_pattern = re.compile(pattern_text)
def __init__(self, str):
self.text_to_search = str
self.text_found = ""
def search_tax_id(self):
matched_p_taxidnumber = PropertyTaxIDNumber.regex_pattern.search(self.text_to_search)
print(matched_p_taxidnumber)
self.text_found = matched_p_taxidnumber.group()
return self.text_found
def scan_txt_report(fli):
data_retrieved = []
file_input = open(fli, mode='r', encoding='utf-8')
property_report = file_input.read()
property_number = PropertyNumber(property_report)
data_retrieved.append(property_number.search_p_number())
property_coefficient = PropertyCoefficient(property_report)
data_retrieved.append(property_coefficient.search_p_coefficient())
property_tax_id_number = PropertyTaxIDNumber(property_report)
data_retrieved.append(property_tax_id_number.search_tax_id())
return data_retrieved
def main():
if os.path.exists("./notas_simples/ns_txt"):
os.chdir("./notas_simples/ns_txt")
list_txt_files = glob.glob("*.txt")
print(list_txt_files)
with open("..\..\listado_de_fincas.csv", mode='w', newline='') as fiout:
file_writer = csv.writer(fiout, delimiter=';')
for file_name_input in list_txt_files:
data_line = scan_txt_report(file_name_input)
file_writer.writerow(data_line)
if __name__ == '__main__':
main()
# TODO Idufir: "(?<=IDUFIR: )\d{14}"
# TODO calle: "(?<=Calle ).*" Break down in street name and number of address
# TODO piso: "(?<=piso ).*," Break down in floor number and door number (or letter), without boundaries
# TODO titularidad: "(?<=TITULARIDAD\n\n).*" Break down in owner name, VAT number, % and domai type.
As you can see above, the 3 classes I've already wrote: PropertyNumber(object), PropertyCoefficient(object) and PropertyTaxIDNumber(object), has a lot of repeated code. Thus, when I add some regex patterns to each class will be worse.
Yes, you are repeating much of your code, and yes, it is a sign of a weak design. I'll take this as an OOP exercise, because this is an overkill.
First, we can see that the only difference between the different classes is their essence, and their regex pattern. So we can have a base class which handles all the repetitive code. Now each subclass simply handles the different pattern:
class BaseProperty(object):
def __init__(self, search_str, pattern):
self.text_to_search = search_str
self.text_found = ""
self.regex_pattern = re.compile(pattern)
def search_property(self):
matched_property = self.regex_pattern.search(self.text_to_search)
print(matched_property)
self.text_found = matched_property.group()
return self.text_found
class PropertyNumber(BaseProperty):
def __init__(self, search_str):
super(PropertyNumber, self).__init__(search_str, "(?<=FINCA Nº: )\w{3,6}")
class PropertyCoefficient(BaseProperty):
def __init__(self, search_str):
super(PropertyCoefficient, self).__init__(search_str, "(?<=Participación: )[0-9,]{1,8}")
Second, it doesn't appear that you're actually using the self.text_found field, so why store it? Now you can init all the properties in a single place, and make your scan_txt_report much simpler.
class BaseProperty(object):
def __init__(self, pattern):
self.regex_pattern = re.compile(pattern)
def search_property(self, search_str):
matched_property = self.regex_pattern.search(search_str)
print(matched_property)
return matched_property.group()
...
class PropertyCoefficient(BaseProperty):
def __init__(self):
super(PropertyCoefficient, self).__init__("(?<=Participación: )[0-9,]{1,8}")
properties = [PropertyNumber(), PropertyCoefficient(), ...]
def scan_txt_report(fli):
file_input = open(fli, mode='r', encoding='utf-8')
property_report = file_input.read()
data_retrieved = [prop.search_property(property_report) for prop in properties]
return data_retrieved
And unless you add some specific functionality for each subclass, you can even let go of the specific properties classes, and just do like this:
properties = [BaseProperty("(?<=FINCA Nº: )\w{3,6}"), BaseProperty("(?<=Participación: )[0-9,]{1,8}")]
And one last thing - please see the comment by #JonClements - it's a bad idea to use reserved words (such as str) as variable names.
There is no need for so many classes.It can be done via two classes.
Class Property(object,regex):
#def __init__ ...
#def prepare (This method will prepare return compiled form of regex
Class Search(object,compiled_regex):
#def __init__ ...
#def search ... (same function as now)
def scan_txt_report(fli):
data_retrieved = []
file_input = open(fli, mode='r', encoding='utf-8')
#take a csv containing all the regex.
#for all regex call property and search classes.keep appending results as well.
return data_retrieved
This way the only thing we need to change is the csv.The program remains intact and tested.
For adding new regex's the csv needs to be updated.

python unbound method again

This gets me into difficult time (sorry, i am still very new to python)
Thank you for any kind of help.
The error
print Student.MostFrequent() TypeError: unbound method
MostFrequent() must be called with
Student instance as first argument
(got nothing instead)
This Student.MostFrequent() is called all the way in the end (last line) and the def is last def in the class
EDITED - Naming convention
My long code
import csv
class Student:
sports = []
ftopics = []
stopics = []
choice_list = []
choice_dict = {}
def __init__(self, row):
self.lname, self.fname, self.ID, self.gender, self.sport, self.movie, self.movieyr, self.country, self.ftopic, self.stopic = row
self.sports.append(self.sport)
self.ftopics.append(self.ftopic)
self.stopics.append(self.stopic)
def print_information(self):
return (self.lname, self.fname, self.ID, self.gender)
def print_first(self):
return (self.lname, self.fname, self.sport)
def print_second(self):
return (self.lname, self.fname, self.movie, self.movieyr)
def print_third(self):
return (self.lname, self.fname, self.country)
def print_fourth(self):
return (self.lname, self.fname, self.ftopic, self.stopic)
def most_frequent(self):
for choice in self.choice_list:
self.choice_dict[choice] = self.choice_dict.get(choice, 0) + 1
self.mostFrequent = sorted([(v, k) for k, v in self.choice_dict.items()], reverse=True)
print self.mostFrequent
reader = csv.reader(open('new_mondy_csc_data_revise.csv'), delimiter=',', quotechar='"')
header = tuple(reader.next())
print "%-17s|%-10s|%-6s|%s" %header[:4]
print "-" * 45
students = list(map(Student, reader)) # read all remaining lines
for student in students:
print "%-17s|%-10s|%-6s|%3s" % student.print_information()
print "%-17s|%-10s|%s" %(header[0],header[1],header[4])
print "-" * 45
for student in students:
print "%-17s|%-10s|%s" %student.print_first()
print "%-17s|%-10s|%-16s|%s" %(header[0],header[1],header[5],header[6])
print "-" * 45
for student in students:
print "%-17s|%-10s|%-16s|%s" % student.print_second()
print "%-17s|%-10s|%s" %(header[0],header[1],header[7])
print "-" * 45
for student in students:
print "%-17s|%-10s|%s" %student.print_third()
print "%-17s|%-10s|%-15s|%s" %(header[0],header[1],header[8],header[9])
print "-" * 45
for student in students:
print "%-17s|%-10s|%-16s|%s" % student.print_fourth()
k = len(students)
# Printing all sports that are specified by students
for s in set(Student.sports): # class attribute
print s, Student.sports.count(s), round(((float(Student.sports.count(s)) / k) *100),1)
# Printing sports that are not picked
allsports = ['Basketball','Football','Other','Baseball','Handball','Soccer','Volleyball','I do not like sport']
allsports.sort()
for s in set(allsports) - set(Student.sports):
print s, 0, '0%'
Student.choice_list = Student.sports
X = Student()
X.most_frequent()
#class Search(Student):
# def __init__(self):
# Student.__init__
first read PEP-8 on naming conventions:
Method Names and Instance Variables
Use the function naming rules: lowercase with words separated by
underscores as necessary to improve readability.
second you are calling mostFrequest on the class Student, not an instance of it. Use the method on an instance instead:
student = Student(row)
student.MostFrequent()
use Student().MostFrequent()
edit:
beware that you use class attributes and this is dangerous. here an example:
>>> class Person:
... name = None
... hobbies = []
... def __init__(self, name):
... self.name = name
...
>>> a = Person('marco')
>>> b = Person('francesco')
>>> a.hobbies.append('football')
>>> b.hobbies
['football']
>>> a.name
'marco'
>>> b.name
'francesco'
>>> a.name = 'mario'
>>> b.name
'francesco'
>>> a.name
'mario'
>>>
as you can see i modify marco's hobbies and francesco's hobbies are modified consequentially.
What you probably want is to define most_frequent as a classmethod:
#classmethod
def most_frequent(cls):
for choice in cls.choice_list:
cls.choice_dict[choice] = cls.choice_dict.get(choice, 0) + 1
cls.mostFrequent = sorted([(v, k) for k, v in cls.choice_dict.items()], reverse=True)
return cls.mostFrequent
First, I recommend making function names lower case only.
The error you get results from the usage of MostFrequent as a static method. For this to work, you need to explicitly pass an instance of Student as first argument.
If called directly on an instance of Student, the instance will implicitly be passed as first argument.
Consider using the staticmethod decorator for static usage of functions.
You only rarely call methods on a class definition (Student)
Almost always, you create an instance of the class
someStudent = Student(someRow)
Then you call the method on the instance ("object"), someStudent.
someStudent.MostFrequent()
Student.MostFrequent means You're trying to use static method, not instance method. So You must first create instance by calling Student() and then call MostFrequent() on it.
P.S.: If this is not part of some arcane project, I urge you to follow PEP 8 and use most_frequent as method name.
in your class def, the method definition
def MostFrequent(self,mostFrequent):
has the extra variable mostFrequent that you probably don't want there. Try changing to :
def MostFrequent(self):

Categories