is there any class method defined that can delete an object - python

i am begineer in python and i am creating a employee managment system in which class employer should have a method to delete a object but it seems impossible in python. i have tried many ways to do that but none of them worked for me. the only way it worked when i used del obj_name outside a class body which i clearly dont want
class Employee:
bonus = 1000
def __init__(self,name ,salary,lang,post):
self.name = str(name)
self.salary = int(salary)
self.lang = str(lang)
self.post = str(post)
def deleteAttr(self,attr):
delattr(self, attr)
def getInfo(self):
print(f"Name of the person is {self.name}")
print(f"Language of the person is {self.lang}")
print(f"Post of the person is {self.post}")
def getSalary(self):
print(f"The salary for {self.name} is {self.salary} ")
def inc(self,inc):
self.salary = int(inc) + self.salary
print(f"Incremented salary is {self.salary}")
def dec(self,dec):
self.salary = self.salary - int(dec)
print(f"Decremented salary is {self.salary}")
class Employer(Employee):
#staticmethod
def fired(a):
atr = a.__dict__
for i in list(atr):
delattr(a, i)
del a
def recruit(self,s,name, salary, lang, post):
s = Employee(name, salary, lang, post)
def __init__(self,name,salary,lang,post):
super().__init__(name,salary,lang,post)
def setBonus(self,incr):
self.__class__.bonus += incr
print(f"Bonus increased to {self.__class__.bonus}")
def incBonus(self):
self.bonussalary = self.salary + self.bonus
in this fired is the method i am trying to define where a is the object that i am willing to delete. i read somewhere that deleting all the attributes of an object can help in deleting it but even that is not working.please help me.Thanks in advance

This screams of an XY problem, so I'm not going to answer the question asked, and instead make notes on your design:
There is no reason for an Employer to be a subclass of Employee. Subclass relationships should follow an "is-a" relationship; unless an employer is an employee, it makes no sense to model the relationship that way (Dog can subclass Animal because a dog is an animal, but this doesn't apply to your scenario).
Deleting objects is easy. Just let the last name referring to the object go out of scope, directly or indirectly (or del it if you must), and the object goes away.
But you don't want to delete it, you want them to be fired and removed from the collection of employees from that employer. So don't model employers as employees, make them their own class that contains employees. And don't make fired a static method; that implies an employee is fired by all possible employers at once, not fired by a particular employer.
A reasonable model for Employer might be:
class Employer:
def __init__(self, bonus=1000):
self.employees = []
self.bonus = bonus # bonus argument and attribute only used if bonus is same for all employees of this employer, see below
def recruit(self, name, salary, lang, post): # Remove s, assignment won't work
self.employees.append(Employee(name, salary, lang, post))
return self.employees[-1] # Give caller reference to new employee if they need it
def fire(self, employee):
self.employees.remove(employee) # Will raise exception if employee doesn't work for this employer
def setBonus(self, employee, incr): # If this applies to one employee at a time
employee.bonus += incr
print(f"Bonus increased to {employee.bonus}")
def setBonus(self, incr): # If this applies to all employees for this employer
self.bonus += incr
print(f"Bonus increased to {self.bonus}")
def incBonus(self, employee):
# Little unclear what this was supposed to do; bonussalary wasn't an attribute for
# anything except here in original code; I'll interpret it as returning the salary
# inc(luding) bonus for the year for a given employee
return employee.salary + self.bonus # If bonus applies to all employees
return employee.salary + employee.bonus # If bonus applies per-employee
If you want to hide the Employee objects from the caller, you can have self.employees be a dict mapping a unique ID (e.g. generated via an itertools.count initialized in the constructor), and have recruit insert the new employee keyed by the unique ID, then return only the unique ID, and all other methods would accept that ID instead of the Employee object, look up the correct employee, and perform the work; this way, a given employee object is not directly exposed to the caller; when the employer fires them, they disappear. It's weird (people aren't supposed to live or die by their employment status), but you do you.
Just for completeness, a cleaned up Employee that matches the design for Employer, with the assumption that bonuses are company-wide (not per-employee):
class Employee:
def __init__(self, name, salary, lang, post):
self.name = str(name)
self.salary = int(salary)
self.lang = str(lang)
self.post = str(post)
def print_info(self): # Not named get; get implies you *return* the info; use snake_case over camelCase per Python's PEP8 style guide
print(f"Name of the person is {self.name}")
print(f"Language of the person is {self.lang}")
print(f"Post of the person is {self.post}")
def print_salary(self): # See above for name change reason
print(f"The salary for {self.name} is {self.salary}")
def give_raise(self, amount_raised): # Much more explanatory name than "inc"
self.salary += int(amount_raised) # Avoid repeating self.salary using +=
print(f"Incremented salary is {self.salary}") # Disagree with printing when this happens, but you do you I guess
def cut_pay(self, amount_lost): # Again, explanatory names are good (makes it clear value expected to be positive
self.salary -= int(dec) # Avoid repeating self.salary using -=
print(f"Decremented salary is {self.salary}")
# If not for the prints in each, I'd just implement this as:
return self.give_raise(-amount_lost)
# to reduce code duplication, or replace both functions with a single
# function, "adjust_salary" that takes a positive value for raises,
# negative for cuts, but leaving it separate due to prints contradicting

Related

missing 1 required positional argument (emp1)

I have written code to count employees using class but the the code doesn't work.
class employee:
empCount = 0;
def employee(self, name, salary):
self.name = name;
self.salary = salary;
employee.empCount += 1;
def displayCount(self):
print ("\nTotal Employee %d", Employee.empCount);
def displyEmployee(self):
print('Name:',self.name,'Salary:',self.salary);
emp1 = employee('ABS', 2000)
Your code has a number of problems that are preventing it from working as you expect:
You did not declare your constructor properly. When you do employee('ABS', 200), Python looks for a function called __init__ on the class object. You declared your constructor as employee, similarly to how you would do so in C-based languages. This won't work.
You store the employee count as a variable scoped to the class object. You can do this but I wouldn't because it's a misuse of that capability. Instead, you should create a list of employees and get the length of the list.
Instead of declaring display functions, you should overload the __str__ function, which returns a string representing the object.
Class names should be PascalCase (this doesn't keep your code from working but you should definitely address it).
class Employee:
def __init__(self, name, salary):
self.name = name
self.salary = salary
def __str__(self):
return f"Name: {self.name}, Salary: {self.salary}"
employees = []
employees.append(Employee('ABS', 200))
len(employees) # 1

Why I need to add a square bracket when I pass an instance as a parameter to class?

Below is my code:
class Person():
def __init__(self,name):
self.name=name
self.pet=None
def print_name(self):
print(f"The person's name is {self.name}")
class Employee(Person):
raise_amt=1.04
def __init__(self,name,salary):
super(Employee,self).__init__(name)
self.salary=salary
def apply_raise(self):
self.salary=int(self.salary*self.raise_amt)
class Manager(Person):
def __init__(self,name,salary,employees=None):
super().__init__(name)
self.salar=salary
if employees==None:
self.employees=[]
else:
self.employees=employees
def add_emp(self,emp):
if emp not in self.employees:
self.employees.append(emp)
def print_emps(self):
for emp in self.employees:
emp.print_name()
When I try to run the program with below code, the error will pop up.
frank=Employee("Frank",120000)
john=Employee("John",10000)
sean=Manager("Sean",20000,frank)
sean.add_emp(john)
sean.print_emps()
The error I receive is TypeError: argument of type 'Employee' is not iterable.
However, when I put the square bracket around [frank], the error is gone.
Can you help me to understand the reason?
As others have said, in the Manager class __init__ method, you allow an optional list of Employees to be given. If this list is given then the Manager instance will set it as the employees variable else it will set an empty list. In your case, you are initializing the Manager class with an instance of Employee and not a list.
For the future...
I recommend a few code style changes to help avoid these kind of issues:
Add type annotations. This is not only great for you reading back your code, it enables linters to catch type errors before you run the code.
Add more whitespace. Add spaces between operators, variables, parameters, functions, etc. It makes reading the code much easier.
Use keyword arguments. In the example below, it's much easier to see what each argument is for and by extension, you can see employees is clearly a list.
from typing import Optional, List
class Manager(Person):
def __init__(self, name: str, salary: int, employees: Optional[List[Employee]] = None):
super().__init__(name)
self.salary = salary
if employees is None:
self.employees = []
else:
self.employees = employees
def add_emp(self, emp: Employee):
if emp not in self.employees:
self.employees.append(emp)
def print_emps(self):
for emp in self.employees:
emp.print_name()
And then when you're calling the classes:
frank = Employee(name="Frank", salary=120000)
sean = Manager(name="Sean", salary=20000, employees=[frank])
Python is expecting employees to be a list of items, not a single item. Adding the square brackets around "frank" turns it from a simple Employee object to a list of Employee objects, with the first item being "frank".
You have differing code between your __init__ and add_emp methods. In __init__, you set employees to the value specified (making it in your case an Employee object), whereas in add_emp you use append() to add the value to the existing values, maintaining the variable as a list.
Let's examine what your code actually does here:
First you create two instances of Employee with names (strings) and salaries (ints)
Then you create a Manager with a name, salary, and a single employee object assigned to self.employees
You then check if the "John" employee is in sean's employees variable, but sean's employees variable is not a list of employees, it's just a single employee (frank). You're checking if john is in frank, not if john is in a list of items that currently includes frank.
If you only want to pass a single employee when creating each manager the best fix would be to change your __init__ method as follows:
def __init__(self,name,salary,employees=None):
super().__init__(name)
self.salar=salary
self.employees = []
if employees:
self.employees.append(employees)
If you want to pass in multiple employees, then do exactly as you are currently, pass a list e.g. [frank] not frank to the method.

How can I switch a instance from one class to another in python?

I'm new to programming and just learned about the concept of object oriented programming.
I'm trying to find a way to modify a existing instance make it belong to another class, by this I mean inheritance most of its property but not methods.
Like for us human to get a new job (have different method than the old one) but still is the same person (retain some property like age, name and so on).
I'm not good at describing my question, so I'll paste my example code down here.
from random import randrange
class human(object):
def __init__(self,name,age) -> None:
self.name= name
self.age = age
self.job = False
def introduce(self):
print(f"My name is {self.name}, I'm {self.age} years old now.")
def getjob(self,tfunc):
return tfunc(self.name,self.age)
class teacher(human):
def __init__(self,name,age) -> None:
self.name= name
self.age = age
self.job = 'teacher'
self.studentamount = randrange(12,24)
def introduce(self):
print(f"My name is {self.name}, I'm a {self.job} and have {self.studentamount} students. I'm {self.age} years old now.")
# imagine more method has same name as those in worker class down here.
class worker(human):
def __init__(self,name,age) -> None:
self.name= name
self.age = age
self.job = 'worker'
self.workhour = randrange(8,12)
def introduce(self):
print(f"My name is {self.name}, I'm a {self.job} and I work {self.workhour} hour per day. I'm {self.age} years old now.")
# imagine more method has same name as those in teacher class down here.
a = human('foo',31)
a.introduce()
a.age += 1
a = a.getjob(worker)
a.introduce()
a.age += 8
a = a.getjob(teacher)
a.introduce()
Output will be:
> My name is foo, I'm 31 years old now.
> My name is foo, I'm a worker and I work 9 hour per day. I'm 32 years old now.
> My name is foo, I'm a teacher and have 15 students. I'm 40 years old now.
Some backgrounds:
I was writing a "AI" for a minigame as coding practice, I want the "AI" have very different behavior sometimes.
Such as when take certain amount of damage become defensive which have different sets of movement and never think about attack at all, only 10 or so turns after will it fallback to the default behavior.
And I want to call instance.act() inside the game loop for every "AI", instead of instance.act_default() and instance.act_defensive().
So I think that so call "polymorphism" from what I just learn fit this perfectly, then I encountered this problem.
What I'm seeking is something like getjob() in my example code, but with less jank.
I would imagine when the class get more property, the getjob() method will become really clustered and unreadable and that's not what I want.
And maybe not creating a new human instance replacing the old one every time when that human gets a new job.
And maybe instead of a = a.getjob(teacher) use something like a.getjob(teacher) and self = tfunc(self.name,self.age) inside getjob() method (which I try and will not work).
I edit this because my original question is too vague and not specific.
I change as little as I can, to describe what I'm trying to achieve more clearly. And I provide some background as I hope it would be helpful.
When you have a subclass you can use super() to use the superclass init to avoid code duplication. Also, now the if statement in introduce has to check if job attribute exists:
class human(object):
def __init__(self,name,age) -> None:
self.name= name
self.age = age
def introduce(self):
if hasattr(self, "job"):
print(f"My name is {self.name}, I'm a {self.job}, I'm {self.age} years old now.")
else:
print(f"My name is {self.name}, I'm {self.age} years old now.")
class teacher(human):
def __init__(self,name,age) -> None:
super().__init__(name, age)
self.job = 'teacher'
If you want to assign variables in getjob more dynamically, you could use __dict__ to get all attributes as a dictionary from the class, but then you have to ignore the extra arguments. Use ** to unpack and catch unassign keyword arguments:
class human(object):
def __init__(self,name,age) -> None:
self.name= name
self.age = age
...
def getjob(self,tfunc):
return tfunc(**self.__dict__)
class teacher(human):
def __init__(self,name,age, **ignore) -> None:
super().__init__(name, age)
self.job = 'teacher'
However, I would instead make the "job" class an attribute of a human. This way you don't have to create a new human each time they change jobs. If you think about it, changing jobs doesn't make you a different "human instance". Maybe something like:
class human(object):
def __init__(self,name,age,job=None) -> None:
self.name= name
self.age = age
self.job = job
steve = human("steve", 21)
steve.job = teacher()
bob = human("bob", 50, worker())

Is this code correct

I have a question to ask, please. Given the code below, can you please let me know why in manager (or in the worker) class why
self.FirstName
gives the same result as
self._firstName
I would have thought that self._firstName would not be accessible in either of the classes (Manager/Worker) since it local to the Employee class and should not be accessible outside it, no ?
Please suggest.
import gc
class Employee(object):
"""Employee Base Class"""
def __init__(self, FirstName, LastName,Age, Role):
super(Employee, self).__init__()
self._firstName = FirstName
self._lastName = LastName
self._age = Age
self._role = Role
#property
def FirstName(self):
return self._firstName
#property
def Age(self):
return self._age
#property
def Role(self):
return self._role
#FirstName.setter
def FirstName(self, value):
self._firstName = value;
pass
#Role.setter
def Role(self, value):
self._role = value;
pass
class Manager(Employee):
"""Manager class"""
def __init__(self, FirstName,LastName,Age):
Employee.__init__(self,FirstName, LastName, Age, 'Manager')
# super(Manager, self).__init__()
def getParents(self):
"""Get parents of the class"""
print(gc.get_referrers(self))
pass
def ManagerInfo(self):
print("FirstName : " + self.FirstName)
print("Role : " + self.Role)
print("Age : " + str(self.Age))
class Worker(Employee):
"""docstring for Worker"""
def __init__(self, FirstName, LastName, Age):
Employee.__init__(self,FirstName, LastName, Age, 'employee')
def getParents(self):
"""Get parents of the class"""
print(gc.get_referrers(self))
pass
def WorkerInfo(self):
print("FirstName : " + self.FirstName)
print("Role : " + self.Role)
print("Age : " + str(self.Age))
pass
# manager = Employee('John','Doe' , 40, 'Manager')
# print("{0}'s age is {1} years.".format(manager.FirstName, manager.Age))
anEmp = Worker('WorkerName', 'LastName', 20)
aManager = Manager('John', 'Doe', 40)
print(anEmp.WorkerInfo())
print(anEmp.getParents())
print("----------------------------")
print(aManager.ManagerInfo())
print(aManager.getParents())
Thanks
why self.FirstName gives the same result as self._firstName
Because you defined FirstName as a property returning self._firstname. What did you expect actually ?
I would have thought that self._firstName would not be accessible in either of the classes (Manager/Worker) since it local to the Employee class
It's not 'local to the Employee class', it's an attribute of Employee instances (it doesn't exist in the Employee class itself).
and should not be accessible outside it, no ?
While prefixing a name with a single underscore denotes an implementation attribute (IOW something that is NOT part of the public API - the equivalent of 'protected' in most mainstream languages), it doesn't prevent access to the attribute. Actually there's absolutely NO enforcement of access restriction in Python, it's all convention (and eventually name mangling for __pseudoprivates names).
Python's philosophy is that we are all consenting adults and are wise enough to not do stupid things like messing with what is clearly labelled as an implementation attribute without accepting full responsability for breaking encapsulation.
can you please let me know what I should be doing in order to make sure that the user can only set the value using the setters and not by doing self._firstName
Nothing more than you already did actually. Re-read the above paragraphs, I already mentionned that Python did NOT enforced access restriction of any kind. self._firstname is prefixed with a single leading underscore, which is the way to tell "this is an implemention detail and not part of the API, you should not be messing with this attribute, you should not even know it exists, so if you break something by messing with it well too bad for you dude, but you're on your own".
so if in case, I have some arbitrary logic that manipulates the value in the setter before setting it, the updated value will not be available if the user just does self._firstName instead of self.FirstName
The chances this would happen are rather low actually (and that's an understatement) but theoritically yes this could happen. But this is totally unrelated since you'd have the very same problem if the user used self.FirstName instead since it would still return the stale value...

Using certain attributes of an object to make other objects in a different class

For a program that creates a timetable for a doctor(specialist) I want to use certain attributes of an object created by a different class to be used in the class that makes the timetable for the doctor.
class makePatient(object):
def __init__(self,name,room):
self.name = name
self.room = room
def getPatient(self):
print(self.name)
print(self.room)
class makeSpecialist(object):
def __init__(self,name,specialization,timetable):
self.name = name
self.specialization = specialization
self.timetable = timetable
def getSpecialist(self):
print(self.name)
print(self.specialization)
print(self.timetable)
class makeAgenda(object):
def addAgenda(self):
self.timetable.append()
#I want to append the name of the patient I have defined here.
print(self.timetable)
patient1 = makePatient("Michael","101")
specialist1 = makeSpecialist("Dr. John","Hematology",[])
What do I do now, to make sure that the name "Michael" gets added to the list [] of specialist Dr. John?
Thanks in advance, I will provide further details if necessary!
I think another approach would be better; you can put the whole makePatient object into the timetable for the specialist:
specialist1 = makeSpecialist("Dr. John", "Hematology", [patient1])
Now you can access the names and other attributes of the patients in a specialist's timetable:
for patient in specialist1.timetable:
print(patient.name)
You can also define a __repr__ method to tell Python how to display an object, rather than the current getPatient:
class makePatient(object):
# ...
def __repr__(self):
return "{0} (room {1})".format(self.name, self.room)
Now when you print the whole timetable:
>>> print(specialist1.timetable)
You get the necessary information:
[Michael (room 101)]
Note also that the classes should probably be called, simply, Patient, Specialist and Agenda; the make is implied.
Finally, you will get errors in makeAgenda.addAgenda as, without an __init__, self.timetable doesn't exist for a makeAgenda object, and an empty append() doesn't do anything anyway.
Classes are often used to represent entities and operations allowed on them, include constructing, or making, new instances of them. Therefore, your classes would be better named simplyPatient, Specialist, andAgenda. The name of the method that constructs a new instance of any class in Python is always__init__().
That said, after creating aPatientand aSpecialistyou could then add patient instances to the specialist's timetable/agenda by passing it to aSpecialistmethod specifically designed for that purpose. In other words, a Specialist "has-a" Agenda instance namedtimetableand to which patients can be added via an appropriately namedadd_to_timetable()method.
Here's what I mean -- note I've modified your code to follow PEP 8 -- Style Guide for Python Code guidelines which I also suggest that you follow:
class Agenda(object):
def __init__(self):
self.events = []
def append(self, event):
self.events.append(event)
class Patient(object):
def __init__(self, name, room):
self.name = name
self.room = room
def get_patient(self):
print(self.name)
print(self.room)
class Specialist(object):
def __init__(self, name, specialization):
self.name = name
self.specialization = specialization
self.timetable = Agenda()
def add_to_timetable(self, patient):
self.timetable.append(patient)
def get_specialist(self):
print(self.name)
print(self.specialization)
print(self.timetable)
specialist1 = Specialist("Dr. John", "Hematology")
patient1 = Patient("Michael", "101")
specialist1.add_to_timetable(patient1)
I'm not too sure what you're trying to accomplish here with method that just print values or with the makeAgenda class, but here's how you can get Michael in Dr. John's list:
specialist1.timetable.append(patient1.name)

Categories