I'm working on getting better with OOP in Python and I've run into some real hackishness in one program I'm writing. It works, but it's a mess.
Below is a short test example to illustrate. It creates cars of either 0, 2, or 4 windows into a list, and then compares the first element with the rest of the list.
The 3rd method of the first class shows what I'm worried about. I just want to be able to refer to whatever container that particular object is in without having to call it from the parameters each time. It isn't even that bad in this example, but what I'm working on has it in so many places that it's starting to get confusing.
import random
class Car:
def __init__ (self, company, doors, id):
self.company = company
self.doors = doors
self.id = id
def printDoors(self, id):
print 'Car ' + `self.id` + ' has ' + `self.doors` + ' doors.'
def findSameDoors(self, id):
# these next lines are the ones that really bother me
companyAbstract = self.company + 's'
for i in eval(companyAbstract):
if self.id != i.id and self.doors == i.doors:
print 'Car ' + `i.id` + ' does too!'
class Company:
def __init__ (self, types):
self.types = types
def typesToNum(self):
result = []
for i in self.types:
if i == 'sedan':
result.append(4)
elif i == 'convertible':
result.append(2)
else:
result.append(0)
return result
porsche = Company(['sedan', 'convertible'])
honda = Company(['sedan', 'convertible', 'motorcycle'])
porsches = []
for i in range(10):
porsches.append(Car('porsche', random.choice(porsche.typesToNum()), i))
hondas = []
for i in range(10):
hondas.append(Car('honda', random.choice(honda.typesToNum()), i))
porsches[0].printDoors(0)
porsches[0].findSameDoors(0)
Just in case it matters, Python 2.4.3 on RHEL. Thanks!
If I'm understanding your question right, you want to attach the list of cars to the company object:
import random
class Car:
def __init__ (self, company, doors, id):
self.company = company
self.doors = doors
self.id = id
def printDoors(self, id):
print 'Car ' + `self.id` + ' has ' + `self.doors` + ' doors.'
def findSameDoors(self, id):
for i in self.company.cars:
if self.id != i.id and self.doors == i.doors:
print 'Car ' + `i.id` + ' does too!'
class Company:
def __init__ (self, types):
self.types = types
self.cars = []
def typesToNum(self):
result = []
for i in self.types:
if i == 'sedan':
result.append(4)
elif i == 'convertible':
result.append(2)
else:
result.append(0)
return result
porsche = Company(['sedan', 'convertible'])
honda = Company(['sedan', 'convertible', 'motorcycle'])
for i in range(10):
porsche.cars.append(Car(porsche, random.choice(porsche.typesToNum()), i))
for i in range(10):
honda.cars.append(Car(honda, random.choice(honda.typesToNum()), i))
porsche.cars[0].printDoors(0)
porsche.cars[0].findSameDoors(0)
There's more cleanup that could be done to it, but I think that should solve your immediate concern.
Related
I have a class (Student) with different attributes, such as studentId, address, and courses. My str method for the class returns all the information that the user put in. However, for the attributes that are lists, such as courses, the location of the information is printed out instead of the actual information. Here is the code (sorry it's a little long, there's a bunch of classes):
class Person:
__name = None
__age = None
__address = None
def __init__(self, name, age=0, address=None):
self.set_name(name)
self.set_age(age)
self.set_address(address)
def __str__(self):
return 'Name: ' + self.__name + '\n' + \
'Age: ' + str(self.__age) + '\n' + \
'Address: ' + str(self.__address)
def set_name(self, name):
self.__name = name
def get_name(self):
return self.__name
def set_age(self, age):
self.__age = age
def get_age(self):
return self.__age
def set_address(self, address):
self.__address = address
def get_address(self):
return self.__address
class Student(Person):
def __init__(self, name, studentID= None, age= 0, address= None):
super(Student, self).__init__(name, age, address)
self.set_studentID(studentID)
self.__courses =[]
def __str__(self):
result = Person.__str__(self)
result += '\nStudent ID:' + self.get_studentID()
for item in self.__courses:
result += '\n ' + str(item)
return result
def set_studentID(self, studentID):
if isinstance(studentID, str) and len(studentID.strip()) > 0:
self.__studentID = studentID.strip()
else:
self.__studentID = 'NA'
def get_studentID(self):
return self.__studentID
def add_course(self, course):
print('in add_course')
self.__courses.append(course)
def get_courses(self):
for i in range(len(self.__courses)):
return self.__courses[i]
class Course:
__courseName = None
__dept = None
__credits = None
def __init__(self, courseName, dept= 'GE', credits= None):
self.set_courseName(courseName)
self.set_dept(dept)
self.set_credits(credits)
def __str__(self):
return self.get_courseName() + '/' + self.get_dept() + '/' + str(self.get_credits())
def set_courseName(self, courseName):
if isinstance(courseName, str) and len(courseName.strip()) > 0:
self.__courseName = courseName.strip()
else:
print('ERROR: Name must be a non-empty string')
raise TypeError('Name must be a non-empty string')
def get_courseName(self):
return self.__courseName
def set_dept(self, dept):
if isinstance(dept, str) and len(dept.strip()) > 0:
self.__dept = dept.strip()
else:
self.__dept = "GE"
def get_dept(self):
return self.__dept
def set_credits(self, credits):
if isinstance(credits, int) and credits > 0:
self.__credits = credits
else:
self.__credits = 3
def get_credits(self):
return self.__credits
students = []
def recordStudentEntry():
name = input('What is your name? ')
age = input('How old are you? ')
studentID= input('What is your student ID? ')
address = input('What is your address? ')
s1 = Student(name, studentID, int(age), address)
students.append(s1)
s1.add_course(recordCourseEntry())
print('\ndisplaying students...')
displayStudents()
print()
def recordCourseEntry():
courses = []
for i in range(2):
courseName = input('What is the name of one course you are taking? ')
dept = input('What department is your course in? ')
credits = input('How many credits is this course? ')
c1 = Course(courseName, dept, credits)
print(c1)
courses.append(c1)
displayCourses(courses)
return courses
def displayCourses(courses):
print('\ndisplaying courses of student... ')
for c in range(len(courses)):
print(courses[c])
def displayStudents():
for s in range(len(students)):
print()
print(students[s])
recordStudentEntry()
This is how the code above prints out the 'displaying students...' part:
displaying students...
Name: sam
Age: 33
Address: 123 st
Student ID:123abc
[<__main__.Course object at 0x000002BE36E0F7F0>, <__main__.Course object at
0x000002BE36E0F040>]
I know that it is printing out the location because I need to index into the list. However, the length of the list will be different every time. Normally if I wanted to index into a list, for example, to print a list of names, I would do:
listOfNames = ['sam', 'john', 'sara']
for i in range(len(listOfNames)):
print(listOfNames[i])
or
listOfNames = ['sam', 'john', 'sara']
for i in listOfNames:
print(i)
(not sure what if any difference there is between the 2 ways since they both print out the same way:)
sam
john
sara
How can I write something like the indexing into a list technique shown here in my str method for my class so that it prints the information and not the location?
It would be good to keep to the standard conventions for Python, such as naming
private attributes for objects with single underscores, not double underscores.
The latter are reserved for Python "internal" attributes and methods.
Also, it is convention to use object attributes for objects with get/set methods,
not class attributes. This will make it easier to inspect your objects, while
still maintaining data hiding. Example:
class Course:
def __init__(self, courseName, dept= 'GE', credits= None):
self._courseName = None
self._dept = None
self._credits = None
self.set_courseName(courseName)
...
Your question about why the courses don't print out the way you expected
is rooted in a programming error with the way you programmed the recording
of courses. In recordCourseEntry(), you record two courses and put them
in a list. However, you pass that to your Student object using a method
intended for one course at a time. My suggested fix would be:
...
# s1.add_course(recordCourseEntry())
courses = recordCourseEntry()
for course in courses:
s1.add_course(course)
...
This will probably be enough to get you going. An example output I got was:
Name: Virtual Scooter
Age: 33
Address: 101 University St.
Student ID:2021
ff/GE/3
gg/GE/3
I've spent an hour or two collectively now trying to figure this problem out but I'm not getting any results. In my program when a defending animal is "killed" it should be deleted from it's registry, but no matter what I do I can't seem to get that to happen. In this instance if I were to initiate a hunter and a buffalo then attack() the buffalo with the hunter the buffalo should be killed and removed from it's classes registry but I can't get python to select that specific buffalo from the registry.
Any help is appreciated.
class IterRegistry(type):
def __iter__(cls):
return iter(cls._registry)
class Buffalo(object):
__metaclass__ = IterRegistry
_registry = []
hp = 1
price = 150
attacks = 0
regeneration = 2
def __init__(self, name):
self._registry.append(self)
self.name = name
def createBuffalo():
for i in range(len(Buffalo._registry),len(Buffalo._registry)+1):
varname = ("b" + str(i))
globals()[varname] = Buffalo("b" + str(i))
class Wolf:
__metaclass__ = IterRegistry
_registry = []
hp = 1
price = 0
attacks = 2
regeneration = 1.5
def __init__(self, name):
self._registry.append(self)
self.name = name
def createWolf():
for i in range(len(Wolf._registry),len(Wolf._registry)+1):
varname = ("w" + str(i))
globals()[varname] = Wolf("w" + str(i))
class Hunter:
__metaclass__ = IterRegistry
_registry = []
hp = 2
price = 0
attacks = 1
regeneration = 0
balance = 0
def __init__(self, name):
self._registry.append(self)
self.name = name
def createHunter():
for i in range(len(Hunter._registry),len(Hunter._registry)+1):
varname = ("h" + str(i))
globals()[varname] = Hunter("h" + str(i))
def attack(attacker, target):
if attacker.attacks >= 1:
target.hp -= 1
if target.hp == 0:
if type(attacker) == Hunter:
attacker.balance += 150
print(target)
if type(target) == Wolf:
del Wolf._registry[[n for n in Wolf._registry if n == target]]
if type(target) == Hunter:
del Hunter._registry[[n for n in Hunter._registry if n == target]]
if type(target) == Buffalo:
del Buffalo._registry[[n for n in Hunter._registry if n == target]]
One more note, the reason I have double brackets is because it was incorrect syntax with single brackets but the syntax was fine with doubles.
If you need any more code, ask in a comment below (I have not included all of it but it should be enough).
The issue you're trying to solve is caused by trying to use a list as an index to be removed from your list
[n for n in Wolf._registry if n == target]
All you're trying to get out of this list comprehension is the target, so just use that.
Wolf._registry.remove(target)
I am using eval to run a generated string to append the newly created EggOrder instance to the list of the correct instance of the DailyOrders class. The day provided by EggOrder is used to used to append to the correct instance. This relies on eval and the variable name of the DailyOrders instance and so it would be great to get this removed. I know there must be a better way.
class DailyOrders:
PRICE_PER_DOZEN = 6.5
def __init__(self, day):
self.orders = []
self.day = day
def total_eggs(self):
total_eggs = 0
for order in self.orders:
total_eggs += order.eggs
return total_eggs
def show_report(self):
if self.total_eggs() < 0:
print("No Orders")
else:
print(f"Summary:\nTotal Eggs Ordered: {self.total_eggs()}")
print(f"Average Eggs Per Customer: {self.total_eggs() / len(self.orders):.0f}\n*********")
class EggOrder():
def __init__(self, eggs=0, name="", day=""):
if not name:
self.new_order()
else:
self.name = name
self.eggs = eggs
self.day = day
eval(f"{self.day.lower()}.orders.append(self)")
def new_order(self):
self.name = string_checker("Name: ")
self.eggs = num_checker("Number of Eggs: ")
self.day = string_checker("Date: ")
def get_dozens(self):
if self.eggs % 12 != 0:
dozens = int(math.ceil(self.eggs / 12))
else:
dozens = self.eggs / 12
return dozens
def show_order(self):
print(f"{self.name} ordered {self.eggs} eggs. The price is ${self.get_dozens() * DailyOrders.PRICE_PER_DOZEN}.")
if __name__ == "__main__":
friday = DailyOrders("Friday")
friday_order = EggOrder(12, "Someone", "Friday")
friday_order.show_order()
friday.show_report()
saturday = DailyOrders("Saturday")
saturday_order = EggOrder(19, "Something", "Saturday")
saturday_order = EggOrder(27, "Alex Stiles", "Saturday")
saturday.show_report()
DailyOrders isn't actually a superclass (it was in a earlier version), it acts like one and I suspect the answer might have some inheritance.
Me very very new programmer, I'm new to classes and not sure how to set up a print method for this class. How do I go about setting up a print method for my class here? Thanks for anything!
class travelItem :
def __init__(self, itemID, itemName, itemCount) :
self.id = itemID
self.name = itemName
self.itemCount = itemCount
self.transactions = []
def getID(self) :
return(self, id)
def getName(self) :
return(self.name)
def setName(self, newName) :
self.name = newName
def getAvailableStart(self):
return(self.AvailableStart)
def appendTransaction(self, num) :
self.transactions.append(num)
def getTransactions(self) :
return(self.transactions)
def getReservations(self) :
Additions = 0
for num in self.transactions :
if (num > 0) :
Additions = Additions + num
return(Additions)
def getCancellations(self) :
Subtractions = 0
for num in self.transactions :
if (num < 0) :
Subtractions = Subtractions + num
return(Subtractions)
def getAvailableEnd(self) :
total = self.AvailableStart
for num in self.transactions :
total = total + num
return(total)
Remember that a method is called on an instance of a class, so if you mean to create a true method that just prints a class you can write something like
class Foo(object):
def print_me(self):
print(self)
foo_instance= Foo()
foo_instance.print_me()
But it sounds like you want to customize the output of print(). That is what the built in method __str__ is for, so try this.
class Foo(object):
def __str__(self):
# remember to coerce everything returned to a string please!
return str(self.some_attribute_of_this_foo_instance)
a good example from your code might be
...
def __str__(self):
return self.getName + ' with id number: ' + str(self.getId) + 'has ' + str(self.getTransactions) + ' transactions'
You must use a __str__ special method:
class travelItem:
...
def __str__(self):
return "a string that describe the data I want printed when print(instance of class) is called"
Okay, I'm trying to migrate some similar classes underneath a parent to make modifying all the classes at once easier. This is my code:
class CreditCard():
def __init__(self, name, short, tag, length):
self.name = name
self.short = short
self.tag = tag
self.length = length
self.CCnumber = tag
while (len(self.CCnumber) < self.length - 1):
rand_int = random.randrange(10)
rand_str = str(rand_int)
self.CCnumber = self.CCnumber + rand_str
if (length == 15):
d = CheckSumDigit(self.CCnumber)
self.CCnumber = self.CCnumber + d
if (not RigorousVerifyLuhn(self.CCnumber)):
ln = self.length - 1
clip = self.CCnumber[0:ln]
fulfilled = False
dig = 0
while (dig <= 9 and fulfilled == False):
cand = clip + str(dig)
if (RigorousVerifyLuhn(cand)):
fulfilled = True
self.CCnumber = cand
dig = dig + 1
if (fulfilled == False):
if (len(self.CCnumber) != self.length):
print("Invalid " + self.name + " number, LENGTH " + len(self.CCnumber) + " (" + self.CCnumber + ")")
else:
print("Invalid " + self.name + " number, LUHN " + "(" + self.CCnumber + ")")
class AmexCreditCard(CreditCard):
def __init__(self):
self.NAME = 'American Express'
self.SHORT = 'AMEX'
self.TAG = '3'
self.LENGTH = 15
CreditCard.__init__(self, 'American Express', 'AMEX', '3', 15)
class VisaCreditCard():
def __init__(self):
self.NAME = 'Visa'
self.SHORT = 'VISA'
self.TAG = '4'
self.LENGTH = 16
CreditCard.__init__(self, self.NAME, self.SHORT, self.TAG, self.LENGTH)
class MasterCardCreditCard():
def __init__(self):
CreditCard.__init__(self, 'MasterCard', 'MC', '5', 16)
class DiscoverCreditCard():
def __init__(self):
CreditCard.__init__(self, 'Discover', 'DISC', '6011', 16)
I have several different styles of syntax on the child credit cards because I'm not sure of the proper syntax for it. When I run this, I get this error:
TypeError: unbound method __init__() must be called with CreditCard instance as first argument (got VisaCreditCard instance instead)
Can anyone help me fix this error? Thank you!
You forgot to inherit from CreditCard:
class VisaCreditCard(CreditCard):
The same applies to your MasterCardCreditCard and DiscoverCreditCard classes.