Python understanding OOP, inheritance - python

I solve this problem:
Develop an application which operates with next types:
Person (field Name, method ShowData())
Student (field Education)
Worker (field WorkPlace)
Classes Student and Worker are derived from class Person.
Class Academy in it's container collects Students and Workers and shows Name, Education or WorkPlace for all persons in method ShowAll().
We can add new persons to Academy by calling method AddPerson().
Which hierarchy of classes is the best
for solving this problem?
Code should include inheritance and use collections.
This is my solution, but i don't know how to realize method AddPerson:
class Academy(object):
theWholeList = []
#staticmethod
def showAll():
for obj in Academy.theWholeList:
if isinstance(obj,Student):
print obj.name+' - '+obj.edu
elif isinstance(obj,Worker):
print obj.name+' - '+obj.wplace
class Person(Academy):
def __init__(self,name):
self.name = name
super(Person, self).theWholeList.append(self)
def showData(self):
return vars(self)
class Student(Person):
def __init__(self, name, edu):
super(Student, self).__init__(name)
self.edu = edu
class Worker(Person):
def __init__(self, name, wplace):
super(Worker, self).__init__(name)
self.wplace = wplace
Maybe Academy must inherit Person and method AddPerson will be like that:
def add(self,name):
super(Academy,self).__init__(name)

first thing:
class Academy(object):
theWholeList = []
#staticmethod
def showAll():
for obj in Academy.theWholeList:
if isinstance(obj,Student):
print obj.name+' - '+obj.edu
elif isinstance(obj,Worker):
print obj.name+' - '+obj.wplace
you do not need to have Academy's method showAll() be a static method, as on your design the Academy is legitimate to be a singleton, i.e. a class having a single instance.
Also theWholeList is a very bad name for a list. Because you know it is a list, as you're assigning it a list. The name shall describe its semantic, i.e. the kind of things it contains, what it is used for.
You should rewrite it as follows:
class Academy:
def __init__(self):
self.person_list = []
def show_all(self):
for item in self.person_list:
item.show_data()
And you would instanciate it once:
academy = Academy()
Then the following:
class Person(Academy):
def __init__(self,name):
self.name = name
super(Person, self).theWholeList.append(self)
is bad design: in object oriented programming you should think about encapsulating data. Here you're making the assumption that Person knows the internals of Academy. And what if you decide to change Academy's implementation so theWholeList is renamed? Or switched into a dict()? This should be transparent to the "user" of the class Academy. A better design should be:
class Academy:
... # cf earlier
def add_person(self, person):
self.person_list.append(person)
class Person(Academy):
def __init__(self,name):
self.name = name
def show_data(self):
print("My name is: {}".format(name))
So you can use it as follows:
person_a = Person("John Doe")
person_b = Person("Jim Smith")
academy.add_person(person_a)
academy.add_person(person_b)
And finally you're wondering:
Maybe Academy must inherit Person
Most of the time, subclassing is the wrong answer of a wrong question. You need to subclass when you want to extend or specialize behaviour of a class. A classical example would be:
class Animal:
def noise(self):
raise NotImplementedError # virtual method
class Duck(Animal):
def noise(self):
print("quack")
class Cat(Animal):
def noise(self):
print("meaw")
So in your case, you have a class person that implements show_data, and what you want is to extend the behaviour, for worker and student:
class Worker(Person): # a worker _is_ a person!
def __init__(self, name, unit):
# left as an exercise to the OP
def show_data(self):
# left as an exercise to the OP
class Student(Person):
def __init__(self, name, promo):
# left as an exercise to the OP
def show_data(self):
# left as an exercise to the OP
I won't get into much more details here, as I suppose you have a teacher you can ask more about the comments I made. But at least you tried, made some mistakes (AND MISTAKES ARE GOOD!). But I'm not giving you a full answer, my only goal here is to set you up in the right mind set to make your code a better design!
I hope this helps!

You want to be able to add people:
>>> academy = Academy()
>>> academy.add(Person('Pete'))
>>> academy.showAll()
Name: Pete
>>> academy.add(Student('Taras', 'Higher'))
>>> academy.showAll()
Name: Pete
Name: Taras, Education: Higher
>>> academy.add(Worker('riotburn', 'StackOverflow')
>>> academy.showAll()
Name: Pete
Name: Taras, Education: Higher
Name: riotburn, Workplace: StackOverflow
showAll needs to iterate over all people calling ShowData on them. This will be implemented differently for each type.
class Academy(object):
def __init__(self):
self.people = []
def add(self, person):
self.people.append(person)
def showAll(self):
for person in self.people:
person.ShowData()
Where for example, Worker will implement ShowData as:
def ShowData(self):
print 'Name: ' + self.name + ', Education:' + self.edu

Related

object and attributes in python h.w

I have h.w to built a simple class.
I have problems with two def: DisOwnerCars and DisAllOwnerCars.
this is my code (after corrections):
class Car:
def __init__(self, manufacture, production_year):
self.manufacture = manufacture
self.production_year = production_year
def __str__(self):
return '{} {}'.format(self.manufacture, self.production_year)
class CarOwner:
car_owners = []
all_cars = []
def __init__(self, name):
self.name = name
self.cars = []
CarOwner.car_owners.append(self)
def add_car (self, car):
self.cars.append(car)
CarOwner.all_cars.append(car)
def DisAllCars():
for owners in CarOwner.car_owners:
for car in owners.cars:
print(car)
def DisAllCarsSorted():
print(sorted(CarOwner.all_cars, key=lambda x: x.manufacture))
def DisOwnerCars(car_owner):
for car in car_owner.cars:
print(car)
def DisAllOwnerCars():
for owners in CarOwner.car_owners:
print('Cars owned by {}:'.format(owners.name))
for car in owners.cars:
print(car)
jane = CarOwner("Jane")
jane.add_car(Car("Mitsubishi", 2017))
bob = CarOwner("Bob")
bob.add_car(Car("Mazda", 2013))
bob.add_car(Car("BMW", 2012))
DisOwnerCars(jane)
DisAllOwnerCars()
DisAllCarsSorted()
DisAllCars()
I still gets some errors in the DisAllCarsSorted().
the error:
File "", line 3, in DisAllCarsSorted
print(sorted(CarOwner.all_cars))
TypeError: '<' not supported between instances of 'Car' and 'Car'
You seem to get the purpose of classes differently than what it comes to provide.
Classes are prototypes - like reciepe or a blueprint - for creating instance. Therefore, the class CarOwner is a reciepe for what properties and actions can a car owner have. You should not use classes as containers to data, like collecting a list of all owners. That is the use case for lists, and possibly metaclasses.
If you want the list of all car owners, you should create several instances of a CarOwner and keep them in a list, not save all of the data in a set inside the different instances.
I've modified your code a little bit, to provide an example for what does a simple OOP looks like and how to encapsulate the classes; note the abstraction of the class Car, the use of __str__, and the initializer, that do nothing but initialize; I hope it will serve as a good example.
class Car:
def __init__(self, manufacture, production_year):
self.manufacture = manufacture
self.production_year = production_year
def __str__(self):
return '{} {}'.format(self.manufacture, self.production_year)
class CarOwner:
def __init__(self, name):
self.name = name
self.cars = []
def add_car (self, car):
self.cars.append(car)
def print_cars (car_owner):
for car in car_owner.cars:
print(car)
def print_owners (car_owners):
for car_owner in car_owners:
print('Cars owned by {}:'.format(car_owner.name))
for car in car_owner.cars:
print(car)
jane = CarOwner("Jane")
jane.add_car(Car("Mitsubishi", 2017))
bob = CarOwner("Bob")
bob.add_car(Car("Mazda", 2013))
bob.add_car(Car("BMW", 2012))
owners = [jane, bob]
print_cars(jane)
print('===')
print_owners(owners)

class and methods in python. introduction in oop

class Person:
def __init__(self, name):
"""Make a new person with the given name."""
self.myname = name
def introduction(myname):
"""Returns an introduction for this person."""
return "Hi, my name is {}.".format(myname)
# Use the class to introduce Mark and Steve
mark = Person("Mark")
steve = Person("Steve")
print(mark.introduction())
print(steve.introduction())
its suppose to produce
"Hi, my name is Mark." or "Hi, my name is Steve."
but instead it produces
"Hi, my name is undefined."
It should be printing the object's representation in memory (something along the lines of Hi, my name is <__main__.Person object at 0x005CEA10>).
The reason is that the first argument of a method is expected to be the object that the method is called upon.
Just like you have def __init__(self, name): you should have def introduction(self, myname):.
Then you will encounter another problem, as introduction now expects an argument myname which you don't provide. However, it is not needed now since you have access to self.myname.
class Person:
def __init__(self, name):
"""Make a new person with the given name."""
self.myname = name
def introduction(self):
"""Returns an introduction for this person."""
return "Hi, my name is {}.".format(self.myname)
# Use the class to introduce Mark and Steve
mark = Person("Mark")
steve = Person("Steve")
print(mark.introduction())
print(steve.introduction())
Will output
Hi, my name is Mark.
Hi, my name is Steve.
You need to declare introduction() -> introduction(self) as an instance method (by passing in self) to be able to access the instance variable self.myname.
class Person:
def __init__(self, name):
"""Make a new person with the given name."""
self.myname = name
def introduction(self):
"""Returns an introduction for this person."""
return "Hi, my name is {}.".format(self.myname)
Sample output:
# Use the class to introduce Mark and Steve
mark = Person("Mark")
steve = Person("Steve")
print(mark.introduction())
print(steve.introduction())
>>> Hi, my name is Mark.
>>> Hi, my name
Please note however, that the first parameter in a function within a class is reserved for either a class, or object to pass itself to (unless a #staticmethod tag is applied to the method, then the first implicit parameter is not passed; which essentially behave as module methods).
Also keep in mind that self is not a reserved word, so you could name it anything (even though self is PEP convention). The below example executes the same output as the example above, and is semantically the same.
def introduction(myname):
"""Returns an introduction for this person."""
return "Hi, my name is {}.".format(myname.myname)
9.3.5. Class and Instance Variables
Your problem is that your giving your introduction method the parameter myname, but never supplying it with a valid argument.You can simply do:
mark = Person(" Mark")
steve = Person(" Steve")
print(mark.introduction(mark.myname))
print(steve.introduction(steve.myname))
your giving the introduction method, the variable from your class myname.
But the above is not even necessary. Since your initializing your name variable in the __init__ method of your class, it is like a global variable. So you can simply say:
class Person:
def __init__(self, name):
"""Make a new person with the given name."""
self.myname = name
def introduction(self):
"""Returns an introduction for this person."""
return "Hi, my name is{}".format(self.myname)
# Use the class to introduce Mark and Steve
mark = Person(" Mark")
steve = Person(" Steve")
print(mark.introduction())
print(steve.introduction())

How should I handle Python list properties with respect to getters/setters?

It is my understanding that one aspect of the Pythonic way is to use direct member variable access of classes until 'getters/setters' are needed. Code is available on Ideone
class Person():
def __init__(self, name, age=None, friends=None):
self.name = name
self.age = age
if friends is None:
self.friends = []
else:
self.friends = friends
me = Person('Mr. Me')
you = Person('Ms. You')
me.age = 42
me.friends.append(you)
Because of the #property decorator approach this access to member variable age can be 'converted' to a 'getter/setter' interface in the future without rewriting the line that sets it to 42. But what about the last line where I add you to my friends list? Is it possible for the Person class to intercept the append() call and take other actions? Perhaps in the future I would decide to add the feature that you would get notified that they have been added to me's friends list.
Of course, once I ask the question my brain turns on and comes up with a solution. Let me know if this is good or not. Instead of intercepting the .append() call in Person, create a class PersonFriendList(List) and override append() with the desired functionality. Then, instead of assigning [] to self.friends assign PersonFriendList(). The .friend value should probably also get decorated as a #property so that assignment can be intercepted by Person to avoid .friend being written to the wrong kind of list.
Code available on Ideone.
class Person():
def __init__(self, name, age=None, friends=None):
self.name = name
self.age = age
if friends is None:
friends = []
self.friends = PersonFriendList(friends)
class PersonFriendList(list):
def __init__(self, *args):
super(PersonFriendList, self).__init__(*args)
self.DebugPrint('constructed with {}'.format(str(*args)))
def DebugPrint(self, string):
print('{}(): {}'.format(self.__class__.__name__, string))
def append(self, *args):
super(PersonFriendList, self).append(*args)
self.DebugPrint('appending {}'.format(str(*args)))
me = Person('Mr. Me')
you = Person('Ms. You')
me.age = 42
me.friends.append(you)

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)

__init__ and the setting of class variables

Im having some trouble understanding Inheritance in classes and wondering why this bit of python code is not working, can anyone walk me through what is going wrong here?
## Animal is-a object
class Animal(object):
def __init__(self, name, sound):
self.implimented = False
self.name = name
self.sound = sound
def speak(self):
if self.implimented == True:
print "Sound: ", self.sound
def animal_name(self):
if self.implimented == True:
print "Name: ", self.name
## Dog is-a Animal
class Dog(Animal):
def __init__(self):
self.implimented = True
name = "Dog"
sound = "Woof"
mark = Dog(Animal)
mark.animal_name()
mark.speak()
This is the output through the terminal
Traceback (most recent call last):
File "/private/var/folders/nd/4r8kqczj19j1yk8n59f1pmp80000gn/T/Cleanup At Startup/ex41-376235301.968.py", line 26, in <module>
mark = Dog(Animal)
TypeError: __init__() takes exactly 1 argument (2 given)
logout
I was trying to get animal to check if an animal was implemented, and then if so, get the classes inheriting from animal to set the variables that Animals would then be able to manipulate.
katrielalex answered your question pretty well, but I'd also like to point out that your classes are somewhat poorly - if not incorrectly - coded. There seems to be few misunderstandings about the way you use classes.
First, I would recommend reading the Python docs to get the basic idea: http://docs.python.org/2/tutorial/classes.html
To create a class, you simply do
class Animal:
def __init__(self, name, sound): # class constructor
self.name = name
self.sound = sound
And now you can create name objects by calling a1 = Animal("Leo The Lion", "Rawr") or so.
To inherit a class, you do:
# Define superclass (Animal) already in the class definition
class Dog(Animal):
# Subclasses can take additional parameters, such as age
def __init__(self, age):
# Use super class' (Animal's) __init__ method to initialize name and sound
# You don't define them separately in the Dog section
super(Dog, self).__init__("Dog", "Woof")
# Normally define attributes that don't belong to super class
self.age = age
And now you can create a simple Dog object by saying d1 = Dog(18) and you don't need to use d1 = Dog(Animal), you already told the class that it's superclass is Animal at the first line class Dog(Animal):
To create an instance of a class you do
mark = Dog()
not mark = Dog(Animal).
Don't do this implimented stuff. If you want a class that you can't instantiate (i.e. you have to subclass first), do
import abc
class Animal(object):
__metaclass__ = abc.ABCMeta
def speak(self):
...
Since age in the given example is not part of the parent (or base) class, you have to implement the the function (which in a class is called method) in the class which inheritted (also known as derived class).
class Dog(Animal):
# Subclasses can take additional parameters, such as age
def __init__(self, age):
... # Implementation can be found in reaction before this one
def give_age( self ):
print self.age

Categories