Python: Class and SubClass - Why won't in recognize the subclass - python

I am trying to create a class that contains the salary and bonus attributes and another that contains the name and idnum attributes. With a small program that asks if the shift has met goal for the year and then figures the total income for the shift supervisor for the year. Every time I try I get:
File "C:\Python33\12-2.py", line 53, in main
shift1 = Shiftsupervisor.Employee('28000.0','2240.0','Ian McGregor', 'S10001' )
AttributeError: type object 'Shiftsupervisor' has no attribute 'Employee'
What have I done wrong??
# This creates two classes - ShiftSupervisor & Employee
# The program then tells us what the annual income is
# This creates a class of Super Class Shiftsupervisor which contains salary, & bonus \
figures
class Shiftsupervisor:
#Initialize the Shiftsupervisor attributes
def __init__(self, salary, bonus):
self.__salary = salary
self.__bonus = bonus
# creates the mutator for the attributes
def set_salary(self, salary):
self.__salary = salary
def set_bonus(self, bonus):
self.__bonus = bonus
# returns the attributes
def get_salary(self):
return self.__salary
def get_bonus(self):
return self.__bonus
#Create the subclass of employee which holds the name & idnum
#Initialize the employee attributes
class Employee(Shiftsupervisor):
def __init__(self, salary, bonus, name, idnum):
Shiftsupervisor.__init__(self, salary, bonus)
#Initialize the employee new attributes
self.__name = name
self.__idnum = idnum
#creates the new mutator for name & id
def set_name(self, name):
self.__name = name
def set_idnum(self, idnum):
self.__idnum = idnum
# new method returns the name & id
def get_name(self):
return self.__name
def get_idnum(self):
return self.__idnum
#This program take info from the two classes and gives
# the total income for the Shift Supervisor
#Creates the shift supervisor objects
def main():
shift1 = Shiftsupervisor.Employee('28000.0','2240.0','Ian McGregor', 'S10001' )
shift2 = Shiftsupervisor.Employee('29500','2360.0','Brian Bory', 'S20202' )
shift3 = Shiftsupervisor.Employee('28750.0','2300.0''Finn McCool', 'S30045' )
def total_income():
if production == 'y' or 'Y':
return __salary + __bonus
else:
return __salary
#Ask the Question - Did they make production quota
production = input('Did Shift 1 make quota this year? Type Y for yes ' )
#Print the income
print(shift1.get_name(),'s Total income is: $', format(total_income, \
',.2f'), sep='')
#Ask the Question - Did they make production quota
production = input('Did Shift 2 make quota this year? Type Y for yes ' )
#Print the income
print(shift2.get_name(),'s Total income is: $', format(total_income, \
',.2f'), sep='')
#Ask the Question - Did they make production quota
production = input('Did Shift 3 make quota this year? Type Y for yes ' )
#Print the income
print(super3.get_name(),'s Total income is: $', format(total_income, \
',.2f'), sep='')
#call the main function
main()

Your code has the following problems:
I think it'd be better have ShiftSupervisor be a subclass of Employee. Unless I'm misunderstanding, a shift supervisor is a kind of employee, so employee is the base class. A shift supervisor might have additional attributes that specialize the Employee class. I've added the shift_number attribute to demonstrate this.
Your main method only creates the employees, but doesn't ever do anything with them.
Your total_income method is a bit confused. Remember, __salary and __bonus are attributes of an object. You have do always use the form instance.attribute in order to get access to those.
You don't need getters and setters in Python. The convention is to keep them normal fields that are meant to be publicly accessible, and use properties if it turns out you do need more complex accessing logic.
The salary and bonus shouldn't be a string -- they're numeric values.
Taken together, your new code might look something like this:
class Employee:
def __init__(self, salary, bonus, name, idnum):
self.salary = salary
self.bonus = bonus
self.name = name
self.idnum = idnum
class ShiftSupervisor(Employee):
def __init__(self, salary, bonus, name, idnum, shift_number):
super().__init__(salary, bonus, name, idnum)
self.shift_number = shift_number
def main():
shift1 = ShiftSupervisor(28000.0, 2240.0, 'Ian McGregor', 'S10001', 1)
shift2 = ShiftSupervisor(29500, 2360.0, 'Brian Bory', 'S20202', 2)
shift3 = ShiftSupervisor(28750.0, 2300.0, 'Finn McCool', 'S30045', 3)
find_income(shift1)
find_income(shift2)
find_income(shift3)
def find_income(employee):
production = input('Did shift {0} make quota this year? Type Y for yes '.format(employee.shift_number))
if production.lower() == 'y':
total_income = employee.salary + employee.bonus
else:
total_income = employee.salary
print("{0}'s Total income is: ${1}".format(employee.name, total_income))
main()
I'm also getting the feeling that you're entangling a shift and an employee in some way that you shouldn't be, though I'm quite able to put my finger on it. A shift might have more than one employee, and an employee could work multiple shifts, though this'll definitely depend based on what problem you're trying to solve.

Related

Why am I getting 'UnboundLocalError: local variable 'value' referenced'?

I just learned what classes are today so I'm hoping this doesn't look too bad. 'model' refers to the model year.
class Car:
def __init__(self, model=0, price=0, year=0):
self.model = model
self.price = price
self.year = year
def depreciate(self, price):
rate = 0.15
age = da_car.model - da_car.year
for i in range(age):
value = price * rate
price = value
return value
def print_info(self):
print('Car\'s infortmation: \n Model year: ', da_car.model ,' \n Purchase price: ', da_car.price,' \n Current value: ', car_value)
da_car = Car()
da_car.model = int(input())
da_car.price = int(input())
da_car.year = int(input())
car_value = da_car.depreciate(da_car.price)
da_car.print_info()
I'm trying to return the depreciated value of the input price after x amount of years. Not sure why I'm getting this error. I tried making 'value' global but that did not work.
The issue is that the variable "value" actually is defined within the "for" loop, and that is its scope. So that variable and its information is not known outside of that loop. The program would most likely want/need a definition of that variable that would be visible within the class. With that in mind and testing out the code I made a few tweaks to the program. Following is a revised version of your program.
class Car:
def __init__(self, model=0, price=0.0, year=0):
self.model = model
self.price = price
self.year = year
self.value = price # This will be calculated later
def depreciate(self):
rate = 0.15
age = self.year - self.model # Correct age calculation
self.value = self.price # Initialize the value to the initial price
for i in range(age):
self.value = float(self.value - self.value * rate)
#price = value
return self.value
def print_info(self):
# The function within a method would not print data for a specific class (e.g. "da_car")
print('Car\'s infortmation: \n Model year: ', self.model ,' \n Purchase price: ', self.price,' \n Current value: ', self.value)
da_car = Car()
dx_car = Car()
print("First car statistics")
da_car.model = int(input("Model year: "))
da_car.price = float(input("Price: "))
da_car.year = int(input("Current year: "))
car_value = da_car.depreciate()
da_car.print_info()
print("Second car statistics")
dx_car.model = int(input("Model year: "))
dx_car.price = float(input("Price: "))
dx_car.year = int(input("Current year: "))
car_value = dx_car.depreciate()
dx_car.print_info()
Following are some key points.
In order to make the value variable pertinent to the depreciation function and to the car printing function, it was added as an attribute to the class.
In the various class functions, it would not be appropriate to use object specific variables in the calculations; rather class defined variables make more sense - so references to a specific object were changed to the "self" reference.
In testing I noticed the calculation for age was incorrect - the two variables needed to be reversed.
A second object definition was added to accentuate the point of usage of classes and objects and attempt to clarify why one would not want to use object specific variables in a class function.
With those revisions in place, a test run of the program was done.
#Dev:~/Python_Programs/Cars$ python3 Cars.py
First car statistics
Model year: 2020
Price: 32000
Current year: 2025
Car's infortmation:
Model year: 2020
Purchase price: 32000.0
Current value: 14198.57
Second car statistics
Model year: 2018
Price: 44000
Current year: 2025
Car's infortmation:
Model year: 2018
Purchase price: 44000.0
Current value: 14105.391884375002
Some print formatting would be beneficial for the current value but this should highlight the setup and use of objects based upon class definitions.
Give that a try and see if it meets the spirit of your project.

Python error: object has no attribute

I'm new to coding -- taking a Python college course. I know this will be obvious to many of you, but I can not figure out why I continue to get this error attribute error:
prodworker = employee.ProductionWorker(shift_num, pay_rate)
AttributeError: 'Employee' object has no attribute 'ProductionWorker'
Any help is greatly appreciated :)
class Employee: #writing new class called Employee:
def __init__(self, name, number): #accepts arguments for employee name and
self.__name = name #employee number
self.__number = number
def set_name(self, name): #mutator methods to set name and number
self.__name = name
def set_number(self, number):
self.__number = number
#accessor methods returns name and number
def get_name(self):
return self.__name
def get_number(self):
return self.__number
class ProductionWorker(Employee): #write subclass
def __init__(self, shift_num, pay_rate):
Employee.__init__(self, 'ProductionWorker')
self.__shift_num = shift_num
self.__pay_rate = pay_rate
def set_shift_num(self, shift_num):
self.__shift_num = shift_num
def set_pay_rate(self, pay_rate):
self.__pay_rate = pay_rate
def get_shift_num(self):
return self.__shift_num
def get_pay_rate(self):
return self.__pay_rate
#This program creates an instance of Employee Class
#and an instance of Production Worker Class:
again = 'Y'
while again.upper() == 'Y':
print('Enter the following data for the employee: \n')
name = input('What is the employee name?: ')
number = input('What is the employee number? ')
shift_num = input('What is the employee shift number? 1 = Day, 2 = Night :')
while shift_num != '1' and shift_num != '2':
shift_num = input('Invalid entry! Enter 1 for Day shift or 2 for Night shift!')
else:
if shift_num == '1':
shift_num = 'Day'
if shift_num == '2':
shift_num = 'Night'
pay_rate = float(input('What is the employee pay rate? '))
print()
print('This is an instance of the Employee class:')
employee = Employee(name, number)
print('EMPLOYEE: \t\t'+ employee.get_name())
print('EMPLOYEE NUMBER: \t' + employee.get_number())
print()
print('This is an instance of the Production Worker class: ')
prodworker = employee.ProductionWorker(shift_num, pay_rate)
print('SHIFT: \t\t\t' + ProductionWorker.get_shift_num())
print('PAY RATE: \t\t$' + format(ProductionWorker.get_pay_rate(), ',.2f'), sep='')
print('--------------------------------------------')
again = input('Enter Y to add another: ')
if again.upper() != 'Y':
print('Program End')
The ProductionWorker class is a subclass of the Employee class, but that doesn't mean you can call it through an instance of Employee. It's still a top-level class that you should call directly. Try replacing employee.ProductionWorker(...) with just ProductionWorker(...).
You'll get past the current error, but you may have new ones. For instance, I think the current attempt to call Employee.__init__ from ProductionWorker.__init__ will fail because it doesn't pass the right number of arguments. You may also have logic issues, if you expected employee.ProductionWorker to create a ProductionWorker instance that was related in some way to the employee object.
I'd also discourage you from using __double_leading_underscore names for your attributes. That invokes Python's name mangling system, which is mostly intended to help prevent accidental reuse of the same name from different classes in a large or unpredictable inheritance hierarchy. If you just want your attributes to be "private", use a single underscore. That doesn't protect them from being accessed by outside code, but it serves as documentation that they're not part of the object's public API. One of Python's design philosophies is that it's programmers are responsible for their own behavior (often described with the phrase "We're all consenting adults"). If a programmer wants to access an object's private data they can do so (which can be very useful for debugging). But if they break stuff, they have no one to blame but themselves.

How do I dynamically name an instance or change the attributes of all instances in a class?

I am trying to model population growth using individual agents, but have already run into trouble with the basic skeleton of the model.
I have a class to hold individuals
class Individual:
"""These are individuals"""
list = []
def __init__(self, name, sex, dob):
self.name = name
self.sex = sex
self.dob = dob
self.list.append(name)
def __str__(self):
return "name is {}, sex is {}, age is {}" .format(self.name, self.sex, curyear - self.dob)
and I instantiate new individuals through
def birth():
global person
person = Individual((next(name)), randsex(), curyear)
The basic growth loop is
for i in range(totalyears):
curyear = curyear + 1
if curyear - person.dob == 18:
birth()
else:
pass
The problem seems to be that
if curyear - person.dob == 18:
birth()
is only ageing and checking the last instance of Individual that's created.
print (Individual.list) shows that my final population = starting population + total years /18 and print (str(person)) too seems to confirm this.
I think this is because my birth() function basically names each new instance 'person', so whenever I use person.whatever it references the latest instance created. It seems to me that there are two possibilities.
A) Dynamically give each new instance a unique name, and use a list to reference each of these instances' attributes in the growth loop.
B) Add an age instance attribute and figure out how to change this for all members of the class.
I don't know if either is possible, or how to do them. I would appreciate any advice and examples!

If = 1 then answer is day shift else answer is night shift

with this program the outcome should display employee name, number , shift (day or night based on the number imputed) and hourly pay rate.
I've tried a few different ways and can "almost" get the desired results. Any assistance would be appreciated.
# This Employee class holds general data about employess and will
# end up as the superclass for this example.
class Employee:
#__init__ method initialzes the attributes.
def __init__(self, emp_name, emp_number):
self.__emp_name = emp_name
self.__emp_number = emp_number
# The set_emp_name method gets the employee name.
def set_emp_name(self, emp_name):
self.__emp_name = emp_name
# The set_emp_name method gets the employee number.
def set_emp_number(self, emp_number):
self.__emp_number = emp_number
# The get_emp_name method returns the employee name.
def get_emp_name(self):
return self.__emp_name
# The get_emp_number method returns the employee number.
def get_emp_number(self):
return self.__emp_number
# The ProductionWorker class holds the general data from superclass Employee
# as well as Employee shift time and pay rate making it a subclass
# of Employee.
class ProductionWorker(Employee):
# __init__ method initializes the attributes.
def __init__(self, emp_name, emp_number, shift, payrate):
# Call the superclass
Employee.__init__(self, emp_name, emp_number)
self.__shift = shift
self.__payrate = payrate
# The set_shift method get employee shift.
def set_shift(self, shift):
self.__shift = shift
# The set_payrate method gets employee hourly pay rate.
def set_payrate(self, payrate):
self.__payrate = payrate
# The get_shift method returns the employee shift.
def get_shift(self):
if self.shift == 1:
self.shift = 'Day shift'
elif self.shift == 2:
self.shift = 'Night shift'
return self.__shift
# The get_payrate method returns the employee hourly pay rate.
def get_payrate(self):
return self.__payrate
# This program will test the Employee superclass and ProductionWorker subclass
# by returning and displaying the gathered information.
import sys
# Get the Employee info.
emp_name = input('Employee Name: ')
emp_number = input('Employee Number: ')
shift = float(input ('Shift Number 1 or 2: '))
payrate = input('Hourly Pay Rate: $')
# Determine True or False for mailing list.
#if shift == 1:
#print('Day')
#else:
#print ('Night')
# Create an instance of the ProductionWorker class.
my_productionworker = ProductionWorker(emp_name, emp_number, shift, payrate)
# Display the object's data.
print('Employee Information')
print('---------------------')
print('Employee Name:', my_productionworker.get_emp_name())
print('Employee Number:', my_productionworker.get_emp_number())
print('Shift:', my_productionworker.get_shift())
print('Hourly Pay Rate:$', my_productionworker.get_payrate())
In your method
def get_shift(self):
if self.shift == 1:
self.shift = 'Day shift'
elif self.shift == 2:
self.shift = 'Night shift'
return self.__shift
you're mixing up .shift and .__shift In the if and elif, it should be .__shift and in the assignments it should be just shift (a local variable, not a member variable) and then you should return that local variable (or maybe just return directly from inside the if and elif, depending on how you feel about multiple exit points).
Also, it's a good practice to include a final else in your if / elif, possibly something like this:
else:
shift = 'Cannot convert {} to a valid shift.'.format(self.__shift)
or
else:
raise Exception('Invalid shift value {}.'.format(self.__shift)
which will alert you to the fact that you fell through all the valid options, and give you a hint as to what value is causing the problem.
BTW, you should not be using double underscore variables in this way and, in general, should not try to write Java code in Python. I'd get rid of the getters and setters and just read and write the member values directly.

How do I access a class instance in a list when there is multiple classes?

I'm a beginning programmer who is building a program that simulates a bank with multiple bank accounts that a user can withdraw/deposit cash, create accounts, get exchange rates, etc. Currently, I'm trying to access a group of instances in my class that are all objects of my account class. The Account Manager class is responsible for managing these account objects and helping to organize them when user input is required. Right now, I'm trying to simulate my 3rd option on my menu which gets info on the account of a user's choice(they must manually put the ID of their account in in order to retrieve information on it, withdraw/deposit cash, etc.). Although I've managed to store all of these class instances in a list, I can't seem to use my get_account method to retrieve these for use. I'll post all of my code below. If you see anything else that is out of place, feel free to let me know.
Code:
# Virtual Bank
# 3/21/13
# Account Manager Class
class AccountManager(object):
"""Manages and handles accounts for user access"""
# Initial
def __init__(self):
self.accounts = []
# create account
def create_account(self, ID, bal = 0):
# Check for uniqueness? Possible method/exception??? <- Fix this
account = Account(ID, bal)
self.accounts.append(account)
def get_account(self, ID):
for account in self.accounts:
if account.ID == ID:
return account
else:
return "That is not a valid account. Sending you back to Menu()"
Menu()
class Account(object):
"""An interactive bank account."""
wallet = 0
# Initial
def __init__(self, ID, bal):
print("A new account has been created!")
self.id = ID
self.bal = bal
def __str__(self):
return "|Account Info| \nAccount ID: " + self.id + "\nAccount balance: $" + self.bal
# Main
AccManager = AccountManager()
def Menu():
print(
"""
0 - Leave the Virtual Bank
1 - Open a new account
2 - Get info on an account
3 - Withdraw money
4 - Deposit money
5 - Transfer money from one account to another
6 - Get exchange rates(Euro, Franc, Pounds, Yuan, Yen)
"""
) # Add more if necessary
choice = input("What would you like to do?: ")
while choice != "0":
if choice == "1":
id_choice = input("What would you like your account to be named?: ")
bal_choice = float(input("How much money would you like to deposit?(USD): "))
AccManager.create_account(ID = id_choice,bal = bal_choice)
Menu()
elif choice == "2":
acc_choice = input("What account would you like to access?(ID only, please): ")
AccManager.get_account(acc_choice)
print(acc_choice)
Menu()
Your Account objects don't actually seem to have ID attributes; instead, they have id attributes. Python is case-sensitive; try changing if account.ID == ID to if account.id == ID.
EDIT:
You are also returning after the very first mismatch. You need to remove one level of indentation from your else block so that you get through the entire loop first, and in fact, your else block shouldn't even be an else block, since you're not actually matching an if; the method should only fail if none of the accounts match the given ID.
EDIT 2:
Also, you're not actually assigning the return value from get_account() to anything, so it's lost. I'm not exactly sure what you expect to happen there.
The error lies in lines 31 and 35. You have written "id" instead of "ID". Fully capitalize those two things such that:
class Account(object):
"""An interactive bank account."""
wallet = 0
# Initial
def __init__(self, ID, bal):
print("A new account has been created!")
self.ID = ID
self.bal = bal
def __str__(self):
return "|Account Info| \nAccount ID: " + self.ID + "\nAccount balance: $" + self.bal
Please let us know if the code works after that.

Categories