Am new to Python and was exploring Classes and Object.
I have created a class,defined few function in it. Then I have created another class and was trying to inherit from the first class but got some error.
Error: class CTC(Salary):
NameError: name 'Salary' is not defined
Base Class:
class Salary:
monthly=0.00
name = ""
def __init__(self,name,monthly):
self.name = name
self.monthly = monthly
def display(self):
print("name: ", self.name, "Monthly Salary: ", self.monthly)
Derived Class:
class CTC(Salary):
tax=0.00
ctc=0.00
def __init__(self,name,monthly,tax):
Salary.__init__(self,name,monthly)
self.tax = tax
def calculateCTC(self):
yearly = monthly*12
totalTax= tax *12
ctc = yearly - totalTax
print("Total CTC: ", self.ctc)
obj = CTC("Rishi",28700.00,1295.00)
obj.display(self)
Can anyone explain me the root cause for the error?
I put all code in one file (with minor modifiactions) and it works form me.
class Salary:
def __init__(self, name, monthly):
self.name = name
self.monthly = monthly
def display(self):
print("name: ", self.name, "Monthly Salary: ", self.monthly)
class CTC(Salary):
def __init__(self, name, monthly, tax):
Salary.__init__(self, name, monthly)
self.tax = tax
self.ctc = 0.00 # create with default value
def calculateCTC(self):
yearly = self.monthly*12 # with `self`
totalTax = self.tax*12 # with `self`
self.ctc = yearly - totalTax # with `self`
print("Total CTC: ", self.ctc)
# without indentation
obj = CTC("Rishi", 28700.00, 1295.00)
obj.display() # without `self`
if you need it in separated files
salary.py
class Salary:
def __init__(self, name, monthly):
self.name = name
self.monthly = monthly
def display(self):
print("name: ", self.name, "Monthly Salary: ", self.monthly)
main.py
from salary import Salary
class CTC(Salary):
def __init__(self, name, monthly, tax):
Salary.__init__(self, name, monthly)
self.tax = tax
self.ctc = 0.00
def calculateCTC(self):
yearly = self.monthly*12 # with `self`
totalTax = self.tax*12 # with `self`
self.ctc = yearly - totalTax # with `self`
print("Total CTC: ", self.ctc)
# without indentation
obj = CTC("Rishi", 28700.00, 1295.00)
obj.display() # without `self`
I formated your code and it works for me.
class Salary:
monthly=0.00
name = ""
def __init__(self,name,monthly):
self.name = name
self.monthly = monthly
def display(self):
print("name: ", self.name, "Monthly Salary: ", self.monthly)
class CTC(Salary):
tax=0.00
ctc=0.00
def __init__(self,name,monthly,tax):
Salary.__init__(self,name,monthly)
self.tax = tax
def calculateCTC(self):
yearly = monthly*12
totalTax= tax *12
ctc = yearly - totalTax
print("Total CTC: ", self.ctc)
obj = CTC("Rishi",28700.00,1295.00)
obj.display(self)
Unlike Java, which forces developer to put each class in each file, Python is more flexible. In Python you can write as much code as you want in a single file, meaning that you can have both of your classes in a single file, without needing to import anything. The diffrence is in importing. Java doesn't need you to import any of your project files, Python on the other hand requires you to import anything that is in external packages, no matter where are they kept. So just import your Salary to the file with CTC.
Related
I am new to python. I try to access the attribute acnt_amount from the class bank_Customer, but throws "AttributeError" error. How to access the attribute of the function getDetails to withdraw with in the class from one function to another function? What is the mistake that i do? Any help will be much appreciated! Thanks in advance!
Code:
class bank_Customer:
def getDetails(self, cname, acnt_no, acnt_type, acnt_amount):
self.cname = cname
self.acnt_no = acnt_no
self.acnt_type = acnt_type
self.acnt_amount = acnt_amount
row = self.cname + "," + str(self.acnt_no) + "," + self.acnt_type + "," + str(self.acnt_amount) + "\n"
file = open('cust_details.csv','a')
file.write(str(row))
file.close()
print('*'*40)
print("Account has been added successfully!")
return self.acnt_amount
def withdraw(self):
cash = int(input("Please enter the amount to be withdrawn: "))
self.acnt_amount = self.acnt_amount - cash
f"balance amount is {balance}"
return balance
base = bank_Customer()
base.withdraw()
Error:
Traceback (most recent call last):
File "C:\Users\kisha\IdeaProjects\Git projects in python\ATM application.py", line 96, in <module>
base.withdraw()
File "C:\Users\kisha\IdeaProjects\Git projects in python\ATM application.py", line 66, in withdraw
self.acnt_amount = self.acnt_amount - cash
AttributeError: 'bank_Customer' object has no attribute 'acnt_amount'
As suggested, an init is required. Also consider setting up some defaults, and look at the use of "getters and setters".
You may gain some insight from the following sample bank account class that I wrote some time ago as an example.
# #Author:srattigan
# #Date:2020-12-10 11:10:33
# #LastModifiedBy:srattigan
# #Last Modified time:2020-12-14 09:50:13
# demo class for inheritance
class BankAccount:
"""Generic Bank Account
"""
acc_num = 100000
def __init__(self, name, balance=0):
self.name = name
self.balance = balance
self.account_num = self.acc_num
BankAccount.acc_num += 1
def deposit(self, amount):
assert amount > 0, ValueError("Must deposit a positive amount")
self.balance += amount
def withdraw(self, amount):
self.balance -= amount
def __str__(self):
rep = f"Bankaccount for {self.name}"
rep += f"\n\tAcc Num: {self.account_num}"
rep += f"\n\tBalance: €{self.balance:.2f}"
return rep
customer = BankAccount("Fred Jones", 99)
print(customer)
You need to def init as your first method. Thats where you specify cname, acnt_no etc.
you need to declare the variables inside the class first.
create the init() constructor to declare and initialize those variables
I am making a custom class that performs basic banking functions.
class Account():
'''
A class to perform some basic banking functions
'''
UserList = {} #Empty dictionary to store (UID: name) for each new instance
def __init__(self, name, balance=0.0, uid=None):
self.name = name #The name of the account holder
self.balance = balance #The initial balance
self.uid = uid #User ID number chosen by account holder
#classmethod
def new_account(cls):
'''
New user can specify details of account through this class method via input()
'''
return cls(
input('Name: '),
int(input('Balance: ')),
int(input('UID: ')),
)
def withdraw(self, amount):
if amount > self.balance:
raise RuntimeError('Amount greater than available balance.')
else:
self.balance -= amount
return print("After a withdrawl of {}, {}'s current balance is {}".format(amount, self.name, self.balance)) #printing balance after withdrawl
def deposit(self, amount):
self.balance += amount
return print("After a deposit of {}, {}'s curent balance is {}".format(amount, self.name, self.balance)) # printing balance after deposit
Basically, a new user is created by creating an instance of the Account() class and it accepts a name, initial balance, and a user ID. I added a class method to take this data in through user input when Account.new_account() is called. What I am now looking to do is store the User ID and name for each instance(account) in an empty dictionary. I have been playing around with this for a few hours, and what I was thinking was something like this
def add_user(self, uid, name):
UserList[int(self.uid)] = self.name
inserted somewhere but I tried implementing this in a few places in my code and it continued to just return an empty dictionary. Could someone help point me in the right direction. Also, the two other things I am trying to implement along with this is a way to prevent users from selecting the same UID and a way to require the UID to be exactly 5 numbers. I am relatively new to Python. Thank you.
You can define a dict as a class variable as you already did, but add the UID as a key to the dict in the __init__ method instead of a separate add_user method so that you can always validate the UID when an object is instantiated, no matter how:
class Account():
users = {}
def __init__(self, name, balance=0.0, uid=None):
if uid in self.users:
raise ValueError("UID '%s' already belongs to %s." % (uid, self.users[uid].name))
if len(uid) != 5 or not uid.isdigit():
raise ValueError("UID must be a 5-digit number.")
self.name = name
self.balance = balance
self.uid = uid
self.users[uid] = self
First noticed that you cant to a "return print(...", remove print.
You can do something like this
class Account():
'''
A class to perform some basic banking functions
'''
UserList = {} #Empty dictionary to store (UID: name) for each new instance
def __init__(self, name, balance=0.0, uid=None):
self.name = name #The name of the account holder
self.balance = balance #The initial balance
self.uid = uid #User ID number chosen by account holder
self.add_user(uid, name)
#classmethod
def new_account(cls):
'''
New user can specify details of account through this class method via input()
'''
return cls(
input('Name: '),
int(input('Balance: ')),
int(input('UID: ')),
)
def withdraw(self, amount):
if amount > self.balance:
raise RuntimeError('Amount greater than available balance.')
else:
self.balance -= amount
return "After a withdrawl of {}, {}'s current balance is {}".format(amount, self.name, self.balance) #printing balance after withdrawl
def deposit(self, amount):
self.balance += amount
return "After a deposit of {}, {}'s curent balance is {}".format(amount, self.name, self.balance) # printing balance after deposit
def add_user(self, uid, name):
self.UserList[int(uid)] = name
a = Account("new user", 100, 1)
a.add_user(2, "new user")
a.add_user(3, "new user")
print(a.UserList)
this will output {1: 'new user', 2: 'new user', 3: 'new user'}
Reference the static variable from the class name:
class Account():
user_list = {}
def __init__(self, uid):
self.uid = uid
Account.user_list[uid] = self
a = Account('uid')
print(a.user_list)
# {'uid': <__main__.Account object at 0x1043e7b38>}
For what it's worth, I think a better approach would be to use 2 classes (for convenience, I'm also using dataclasses to auto-generate some functionality - it doesn't affect the core logic). Then you don't have to worry about static variables at all.
import dataclasses
from typing import Dict
#dataclasses.dataclass
class Account:
uid: str
#dataclasses.dataclass
class Bank:
accounts : Dict[str, Account] = dataclasses.field(default_factory=dict)
def add_account(self, account):
if account.uid in self.accounts:
raise ValueError(f'UID : {account.uid} already exists!')
self.accounts[account.uid] = account
b = Bank()
a1 = Account('a1')
b.add_account(a1)
print(b)
# Bank(accounts={'a1': Account(uid='a1')})
I've never used classes before and I am trying to get a general understanding of how they work with the code example I have below. Im having issues referencing one of the names i define for a class. i just want the program to print out a list of the employee names and salaries stored in the list when the option 2 is entered but it gives me the following error:
Traceback (most recent call last):
File "C:\Scott Glenn\Misc\classes.py", line 31, in
employees[i].displayEmployee
AttributeError: 'str' object has no attribute 'displayEmployee'
class Employee:
'Common base class for all employees'
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print "Total Employee %d" % Employee.empCount
def displayEmployee(self):
print "Name : ", self.name, ", Salary: ", self.salary
def AddNewEmployee():
NewEmployee = raw_input("What is the Employees name: ")
employees.append(str(NewEmployee))
NewEmployeeSalary = raw_input("What is the Employees salary: ")
NewEmployee = Employee(NewEmployee, NewEmployeeSalary)
return employees
#=============================================================================
employees=[]
while(1):
print'Welcome to the Employee Database!'
option = raw_input('Please select 1 to add new employee or 2 to display all current employees: ')
if option=='1':
employees.append(AddNewEmployee())
if option=='2':
for i in range(0,len(employees)):
employees[i].displayEmployee
The AddNewEmployee function is wrong. It's returning a list of a single string when you want to be returning a single object of your custom type Employee.
It should be more like this:
def AddNewEmployee():
#string variable to hold name
NewEmployeeName = raw_input("What is the Employees name: ")
#why make a list? you are appending the result of this function to that list
#employees.append(str(NewEmployee))
#plus this is adding the employee before he's even been created
NewEmployeeSalary = raw_input("What is the Employees salary: ")
#construct using name string and salary string
NewEmployee = Employee(NewEmployeeName, NewEmployeeSalary)
return NewEmployee #return Employee object (to be appended later)
Additionally, you are trying to access displayEmployee() as a field of your class, instead of as a method. Fields don't have parenthesis and methods do (so they can take parameters, though in this case the parenthesis are empty as no parameters are passed).
Finally, note that raw_input returns a string so you should cast to float if that is what you wish your NewEmployeeSalary to be. (Right now it's a string.)
I've updated your code below. The main issue that I saw that you had was that you were using 'employees' as a global and appending to it twice. I moved it out of the AddNewEmployee() function and had that return the new employee which is then appended to 'employees'
Also you weren't calling '.displayEmployees'
Notice the the parentheses that I added to the end.
I hope this helps!
class Employee(object):
'Common base class for all employees'
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print "Total Employee %d" % Employee.empCount
def displayEmployee(self):
print "Name : ", self.name, ", Salary: ", self.salary
def AddNewEmployee():
NewEmployee = raw_input("What is the Employees name: ")
NewEmployeeSalary = raw_input("What is the Employees salary: ")
NewEmployee = Employee(NewEmployee, NewEmployeeSalary)
return NewEmployee
# =============================================================================
if __name__ == "__main__":
employees = []
while True:
print'Welcome to the Employee Database!'
option = raw_input(
'Please select 1 to add new employee or 2 to display all current employees: ')
if option == '1':
employees.append(AddNewEmployee())
if option == '2':
for i in range(0, len(employees)):
employees[i].displayEmployee()
I have researched everywhere and although I find the same concepts, I cannot seem to find an answer to my error.
I did not post before because my account info was forgotten on stack, but I have grown very frustrated with this beginner's error.
class Person(object):
def __init__(self, name, phone):
self.name = name
self.phone = phone
class Employee(Person):
total_salary = 0
#staticmethod
def total_salary(self, salary):
return total_salary
def __init__(self, name, phone, salary):
self.name = name
self.phone = phone
self.salary = salary
class Student(Person):
def __init__(self, name, phone, gpa):
self.gpa = gpa
self.name = name
self.phone = phone
def __str__(self):
reply = ""
reply = "Person " + self.name + " has phone " + self.phone + "\n" + " and is a Student with gpa " + str(self.gpa)
return reply
class Professor(Employee):
def __init__(self, name, phone, salary, clas_teach):
self.clas_teach = clas_teach
self.name = name
self.phone = phone
self.salary = salary
def __str__(self):
reply = ""
reply = "Person " + self.name + " has phone " + self.phone + "\n" + " and is an Employee with salary " + str(self.salary) + "\n"
reply += " and is a Professor assigned to class " + self.clas_teach
return reply
class Staff(Employee):
def __init__(self, name, phone, salary, position):
self.position = position
self.name = name
self.phone = phone
self.salary = salary
def __str__(self):
reply = ""
reply = "Person " + self.name + " has phone " + self.phone + "\n" + " and is an Employee with salary " + str(self.salary) + "\n"
reply += " and is Staff with title " + self.position
return reply
# Create a list of people
People = [ Student("Sandy", "326-8324", 3.65), Student("Jordan", "632-7434", 3.1), \
Professor("Leslie", "985-2363", 50000.00, "Info 501"), \
Staff("Alex", "743-4638", 25000.00, "Editor") ]
# display information about our people
print "These are the people in the university:"
for person in People:
print person
# display the total salaries of all our employees and average GPA
# of all of our students
print
print "Our total university payroll budget is: " + str(Employee.total_salary)
print "Our average student GPA is: " + str(Student.mean_gpa())
Your main misunderstanding is how classes work. In your code, you are calling classes rather than instances of classes:
print "Our total university payroll budget is: " + str(Employee.total_salary)
print "Our average student GPA is: " + str(Student.mean_gpa())
The key thing here is:
Employee.total_salary
Instead you should be doing something like this:
leslie = Professor("Leslie", "985-2363", 50000.00, "Info 501")
print "Leslie's Salary: " + str(leslie.salary)
For this specific case, you want the total payroll, which is the sum of all employee salaries. You need a collection of employees somewhere.
def University():
def __init__(self):
self.employees[]
def add_employee(self, employee):
self.employees.append(employee)
def get_total_payroll(self):
total = 0
for employee in self.employees:
total += employee.salary
return total
then use an instance of that class:
university = University()
university.add_employee(Professor("Leslie", "985-2363", 50000.00, "Info 501"))
university.add_employee(Staff("Alex", "743-4638", 25000.00, "Editor"))
print "Total payroll: " + str(university.get_total_payroll())
Obviously, there are a lot more adjustments you need to make, like sorting betweeen employees and students, etc. But hopefully this is enough to get you started.
There are problems with your overall design that have been covered in other answers, so I will just try to explain why your current code is not working:
You have created a class attribute named total_salary, and then shadowed it with a method of the same name.
You are using a static method, when you need to use a class method.
You need to call total_salary as a method in order to return its value.
These problems can be fixed like this:
class Employee(Person):
_total_salary = 0
#classmethod
def total_salary(cls):
return cls._total_salary
...
print "Our total university payroll budget is: " + str(Employee.total_salary())
But note that the code will still raise an AttributeError, because you are trying to call Student.mean_gpa, which hasn't been defined yet.
Your class Employee has an attribute AND a method with the same name. Calling Employee.total_salary will return the function because in the MRO it's replacing the attribute.
Change:
class Employee(Person):
total_salary = 0
to
class Employee(Person):
total_salary_value = 0
Your staticmethod total_salary in Employee is returning a variable that doesn't exist(because it's looking only in the method scope), you should return the Employee.total_salary_value. Like so:
#staticmethod
def total_salary():
return Employee.total_salary_value
In the __init__ of Employee you are not instantiating the Person that you are inheriting (the same thing in Student(inheriting Person), Professor and Staff(both inheriting Employee)
Use either:
Person.__init__(self,name,phone)
or
super(Employee, self).__init__(name, phone)
Still in the init, you are not adding the salary to the total_salary_value
add this:
Employee.total_salary_value += self.salary
Also, there's no need to use self.name and self.phone in the Student, because Person already has this attribute. (The same for that and other variables in Professor and Staff)
And lastly, Student doesn't have a method called mean_gpa. You have to implement it.
Here's the code:
class Person(object):
def __init__(self, name, phone):
self.name = name
self.phone = phone
class Employee(Person):
total_salary_value = 0
#classmethod
def total_salary(cls):
return cls.total_salary_value
def __init__(self, name, phone, salary):
Person.__init__(self,name,phone)
self.salary = salary
Employee.total_salary_value += self.salary
class Student(Person):
all_gpa = []
def __init__(self, name, phone, gpa):
Person.__init__(self,name,phone)
self.gpa = gpa
Student.all_gpa.append(self.gpa)
def __str__(self):
return "Person {0} has phone {1} and is a Student with gpa {2}".format(self.name,self.phone,self.gpa)
#classmethod
def mean_gpa(cls):
return sum(cls.all_gpa)/len(cls.all_gpa)
class Professor(Employee):
def __init__(self, name, phone, salary, clas_teach):
Employee.__init__(self,name,phone,salary)
self.clas_teach = clas_teach
def __str__(self):
return "Person {0} has phone {1} and is an Employee with salary {2}\n" \
"and is a Professor assigned to class {3}".format(self.name,self.phone,self.salary,self.clas_teach)
class Staff(Employee):
def __init__(self, name, phone, salary, position):
Employee.__init__(self, name, phone, salary)
self.position = position
def __str__(self):
return "Person {0} has phone {1} and is an Employee with salary {2}\n" \
"and is Staff with title {3}".format(self.name, self.phone, self.salary, self.position)
# Create a list of people
People = [ Student("Sandy", "326-8324", 3.65),
Student("Jordan", "632-7434", 3.1),
Professor("Leslie", "985-2363", 50000.00, "Info 501"),
Staff("Alex", "743-4638", 25000.00, "Editor") ]
# display information about our people
print "These are the people in the university:"
for person in People:
print person
# display the total salaries of all our employees and average GPA
# of all of our students
print
print "Our total university payroll budget is: " + str(Employee.total_salary())
print "Our average student GPA is: " + str(Student.mean_gpa())
EDIT:
As #ekhumoro said, it's better to use a #classmethod to return the value.
Another thing, your __str__ could be made more clear using .format()
Here I have a sample code to test python class inheritence.
Here , the base class is ‘Person’ and the ‘Employee’ inherits the base class -‘Person’. Also, there are 2 more subclasses inhertis class ‘Employee’.
I would like to initialize subclass veriables in sub classes - ‘OfficeWorker’ and ‘ProductionWorker’, but I am getting ‘TypeError: __init__() takes 2 positional arguments but 7 were given’.
Need pyhton experts suggestion here to define and initialize subclass variable and correct my code
<snip of error>
#$ ./employee86253.py
Enter the name: sunnily
Enter the address: 41801
Enter the phone: 345
Enter the ID number: 12
Enter the employee type ('S' for salaried or 'H' for hourly): S
Enter the skill type ('eng', 'acc', 'sales', 'mgr'): eng
Enter the monthly salary: 123
Traceback (most recent call last):
File "./employee86253.py", line 110, in <module>
main ()
File "./employee86253.py", line 78, in main
**ERROR:**
**worker = OfficeWorker(worker_name, worker_address, worker_phone, worker_id, skill_type, salary)
TypeError: __init__() takes 2 positional arguments but 7 were given**
================== script =======================
#!/usr/bin/env python
# Base class
class Person:
def __init__ (self, name, address, phone_number):
self.name = name
self.address = address
self.phone_number = phone_number
def get_name(self):
return self.name
def get_address(self):
return self.adress
# subclass, inheriting class - Person
**class Employee (Person):
def __init__(self, id_number):
Person.__init__(self, name, address, phone_number)
self.id_number = id_number**
def get_id_number(self):
return self.id_number
# sub class, inheriting Class - Employee
class ProductionWorker (Employee):
**def __init__(self, shift_number, pay_rate):
super().__init__(id_number)
self.shift_number = shift_number
self.pay_rate = pay_rate**
def get_shift_number ( self ):
return self.shift_number
def compute_pay_for_hours( self, hours):
minutes = hours * 60
return ( minutes * self.pay_rate ) / minutes
def get_pay_rate(self ):
return self.pay_rate
# Subclass, inheriting class - Employee
class OfficeWorker (Employee):
**def __init__(self, skill_type, monthly_salary):
super().__init__(id_number)
self.skill_type = skill_type
self.monthly_salary = monthly_salary**
def get_skill_type(self):
return self.skill_type
def compute_pay_for_weeks(self, weeks):
return (weeks * self.monthly_salary ) / 4
def get_month_salary( self ):
return self.monthly_salary
def main():
# Local variables
worker_name= ''
worker_id = ''
worker_shift = 0
worker_pay = 0.0
skill_type = ''
salary = 0.0
emp_type = 'P'
**# Get data attributes
worker_name = input('Enter the name: ')
worker_address = input('Enter the address: ')
worker_phone = input('Enter the phone: ')
worker_id = input('Enter the ID number: ')
emp_type = input('Enter the employee type (\'S\' for salaried or \'H\' for hourly): ')
if emp_type == 'S':
skill_type = input('Enter the skill type (\'eng\', \'acc\', \'sales\', \'mgr\'): ')
salary = float(input('Enter the monthly salary: '))
worker = OfficeWorker(worker_name, worker_address, worker_phone, worker_id, skill_type, salary)
elif emp_type == 'H':
worker_shift = int(input('Enter the shift number: '))
worker_pay = float(input('Enter the hourly pay rate: '))
worker = ProductionWorker(worker_name, worker_address, worker_phone, worker_id, worker_shift, worker_pay)
else:
print('Invalid employee type')
return**
# Create an instance of ProductionWorker
# Display information
print ('Employee information:')
print ('Name:', worker.get_name())
print ('Address:', worker.get_address())
print ('Phone:', worker.get_phone())
print ('ID number:', worker.get_id_number())
if isinstance(worker,ProductionWorker):
print ('Shift:', worker.get_shift_number())
print ('Hourly Pay Rate: $', \
format(worker.get_pay_rate(), ',.2f'), sep='')
print ('Pay Amount for 5.2 hours: $', \
format(worker.compute_pay_for_hours(5.2), ',.2f'), sep='')
else:
print ('Skill type:', worker.get_skill_type())
print ('Monthly Salary: $', \
format(worker.get_month_salary(), ',.2f'), sep='')
print ('Pay Amount for 2.5 months: $', \
format(worker.compute_pay_for_weeks(10), ',.2f'), sep='')
# call the main function
main ()
Taking the simplest case as an example (but you've got clones of this all over the place):
def __init__(self, id_number):
Person.__init__(self, name, address, phone_number)
where do you think name, address, phone_number will come from? If they're supposed to be arguments to the call to Employee then they must be listed as arguments to its __init__ (and "passed up to it" from further subclasses as needed).