I am pretty new to Python, and I am trying to work out the relationships between classes and inheritage etc.
Let's say that I have a class Student:
class Student():
def __init__(self, name):
self.name = name
def get_name(self):
return self.name
def set_name(self, name):
self.name = name
This student can attend multiple courses:
class Course():
def __init__(self, title):
self.title = title
def get_title(self):
return self.title
def set_title(self, title):
self.title = title
And for each course there can also be multiple students.
So it's a manyToMany relationship.
My first thought is to create a third class called StudentCourse:
class StudentCourse(Student, Course):
student_courses = {}
def __init__(self, student, course):
self.student = student
self.course = course
def add_student_to_course(self, student, course, results):
self.student_courses[student] = {
'course': course,
'results': results
}
def get_student_courses(self, student):
return self.student_courses.get(student)
Is this the correct structure?
If so, my question is: within the Student class, how do I access the student_courses dictionary containing the particular student's courses and results?
EDIT
I want to be able to see which students attend a specific course, as well as get the results for each individual student attending a course.
Looking forward to your responses.
Inheritance is almost certainly the wrong way to express the relationship between Students and Courses. Inheritance indicates an IS-A relationship. Neither your students are courses, nor are your courses students. So IS-A is wrong.
What you probably want is HAS-A. Each Student HAS-A number of Courses they're enrolled in. Similarly, each Course has several Students enrolled. In a database you'd create relationship object to handle the many-to-many relationship, but in Python code you can use data structures instead. A list is a natural pick. Here's a design that adds a list of courses to each student and a list of students to each course:
class Student:
def __init__(self, name):
self.name = name
self.courses = []
def enroll(self, course):
self.courses.append(course)
course.students.append(self)
class Course():
def __init__(self, title):
self.title = title
self.students = []
def enroll_student(self, student):
student.enroll(self)
A note about getters and setters (unrelated to your main question): You usually don't need to write methods like get_name and set_name in Python. Usually you should just use the attribute directly if those methods are not doing anything (like validating the input or translating the output in some way). In other languages it is often a best practice to always write and use such methods even if you don't need them yet, but in Python, if you later decide you do need to do some validation, you can change the implementation of the attribute lookup into a method call using a property.
This is more of an opinion-based question than something that you can get a clear answer to. That being said, given that both Student and Course are two very different entities, being connected only by ownership, there is no point in creating a muxed class out of the two.
Instead, I'd recommend having them both handle their part of the many-to-many relationship in a double-linked fashion, i.e. when you add a Student to a Course, you also inform the Student that they now are part of that course and vice versa. Something like:
class Student(object):
def __init__(self, name):
self.name = name
self.courses = {} # we'll use a dict so that our courses can be mapped to results
def add_course(self, course):
if course not in self.courses:
self.courses[course] = {} # initialize each course with an empty dict
# feel free to use any fields you like, like results etc.
course.add_student(self)
def del_course(self, course):
self.courses.pop(course, None)
course.del_student(self)
def __repr__(self): # just to ease our printouts
return self.name
And:
class Course(object):
def __init__(self, title):
self.title = title
self.students = set() # a set ought to be enough
def add_student(self, student):
self.students.add(student)
student.add_course(self)
def del_student(self, student):
self.students.discard(student)
student.del_course(self)
def __repr__(self): # just to ease our printouts
return self.title
And then you can use them in both directions to establish your many-to-many relationship, e.g.:
# Create some students and courses
john = Student("John Doe")
jane = Student("Jane Doe")
foo = Course("Foo Course")
bar = Course("Bar Course")
And then:
# initial state
print("John courses: {}".format(john.courses)) # John courses: {}
print("Jane courses: {}".format(jane.courses)) # Jane courses: {}
print("Foo students: {}".format(foo.students)) # Foo students: set([])
print("Bar students: {}".format(bar.students)) # Bar students: set([])
# lets add John to Foo:
john.add_course(foo)
print("John courses: {}".format(john.courses)) # John courses: {Foo Course: {}}
print("Foo students: {}".format(foo.students)) # Foo students: set([John Doe])
# lets add Jane to Bar, the other way around
bar.add_student(jane)
print("Jane courses: {}".format(jane.courses)) # Jane courses: {Bar Course: {}}
print("Bar students: {}".format(bar.students)) # Bar students: set([Jane Doe])
# lets add John to Bar as well
bar.add_student(john)
print("John courses: {}".format(john.courses))
# John courses: {Foo Course: {}, Bar Course: {}}
print("Bar students: {}".format(bar.students))
# Bar students: set([Jane Doe, John Doe])
# finally, lets add some info for the Foo course to John
john.courses[foo]["results"] = 94
print("John courses: {}".format(john.courses))
# John courses: {Foo Course: {'results': 94}, Bar Course: {}}
And given that we use native structures - sets and dictionaries - to hold our relationships not only that we automatically guard against duplicated entries, we can also easily (and very fast) check for relationships, too, e.g.:
print("John is in Bar: {}".format(john in bar.students)) # John is in Bar: True
# or:
print("Bar is in John's courses: {}".format(bar in john.courses))
# Bar is in John's courses: True
print("Jane is in Foo: {}".format(jane in foo.students)) # Jane is in Foo: False
Then you can expand on it, e.g. to formalize the results you're storing and similar structures - you can continue developing the API to suit your needs. Good luck!
Related
hi i have two classes Book and Library with a text file (my_text.txt including these data
danial feldroy - two scoops of django
james brown - python3 with examples
in the Book class
class Book():
def __init__(self,author,title):
self.title = title
self.author = author
def __str__(self):
return f"Author: {self.author}, Title: {self.title}"
Library takes one input variable, name, which is a string that represents the name of the library
class Library:
def __init__(self, name):
self.name = name
books = {}
# add_book takes a Book instance, and checks to see if the Book is not in self.books. If it's not
# then a new key value pair is added to the dictionary. If it is in books, then the data stored
# in the key 'available' is updated for that book.
def add_book(self, book_inst):
if (book_inst.author, book_inst.title) not in self.books:
self.books[(book_inst.author, book_inst.title)] = {"checked_out": 0, "available": 1}
else:
self.books[(book_inst.author, book_inst.title)]["available"] +=1
# can_check_out will take a Book instance as input. Checks to see if the book is in the dictionary
# of books. If it is, then it checks to see if the value of available is 1 or more. If it is, then it
# returns True. If it does not, then it returns False. If the book cannot be found, then a string is returned
# to the user to inform them of this occurrence.
def can_check_out(self, book_inst):
book = (book_inst.author, book_inst.title)
if book in self.books:
if self.books[book]["available"] >= 1:
return True
else:
return False
else:
return "The book you are looking for has not been added to this library yet."
f = open('my_text.txt').readlines()
book_list = []
for book in f:
instance = tuple((x.strip() for x in book.split('-')))
book_list.append(instance) # the variable instance should be assigned an instance of the Book class.
There is an instance of the Library class called books; i have to add all of the Book instances to books variable
books = Library("Hatcher Graduate Library")
#i have to write the code here
i need to add data to the Book
i have to access to Book through books like this
print(books.can_check_out(Book('Statistics for all', 'jim james')))
i much appreciate your helps ..
You may want to look into class inheritance, it is basically a connector between two classes, where one derives from the other.
Suppose I have two classes Employee and Student:
class Employee():
def __init__(self, id):
self.id = id # the employee id
...methods omitted...
class Student():
def __init__(self, id):
self.id = id # the student id, different from employee id
...methods omitted...
Now I'd like to create a third class StudentEmployee which simply merges Employee and Student.
However, the goal is that both id are still kept in each inherited class.
Some thing like this:
class StudentEmployee(Employee, Student):
def __init__(self, employee_id, student_id):
Employee.__init__(self, employee_id)
Student.__init__(self, student_id) # overrides employee id
Note that both Student and Employee have the id attribute so in reality one will override the other.
The question:
How can I keep both id as they carry different meaning?
For example, is there some way to protect id from one class from being over-riden by another class.
approach 1
One natural way is to change the class definition to:
class Employee():
def __init__(self, id):
self.eid = id # now "id" changes to "eid"
...attributes names in methods updated as well
class Student():
def __init__(self, id):
self.sid = id # now "id" changes to "sid"
...attributes names in methods updated as well
However, I don't like this approach very much because eid is not as neat as sid.
Moreover, the above example might be too simplistic.
Let's imagine the two classes being "merged" have many shared attribute name, the code refactoring work won't be small.
Any other better ways?
I'm learning and experimenting with Python. How can I pass the contact_list in my second function print_contacts so that it can print the name from the contact_list? I'm sure I am doing something wrong, can anyone please explain why is it so?
class Contact(object):
contact_list = []
def __init__(self, name, email):
self.name = name
self.email = email
return Contact.contact_list.append(self)
# How to pass contact_list to below function?
def print_contacts(contact_list):
for contact in contact_list:
print(contact.name)
To me it doesn't make any sense to have a Contact object also own a contact_list attribute, and even less if it's class-wide instead of instanced. I would do this instead:
class Contact(object):
def __init__(self, name, email):
self.name = name
self.email = email
def __str__(self):
return f"{self.name} <{self.email}>"
# or "{} <{}>".format(self.name, self.email) in older versions of
# python that don't include formatted strings
contacts = []
def print_contacts(contacts: "list of contacts") -> None:
for c in contacts:
print(c)
adam = Contact("Adam Smith", "adam#email.com")
contacts.append(adam)
bob = Contact("Bob Jones", "bob#email.com")
contacts.append(bob)
charlie = Contact("Charlie Doe", "charlie#email.com")
contacts.append(charlie)
print_contacts(contacts)
# Adam Smith <adam#email.com>
# Bob Jones <bob#email.com>
# Charlie Doe <charlie#email.com>
Or alternatively, model an AddressBook that knows how to create Contact objects and display them all.
class AddressBook(list):
def add_contact(self, *args, **kwargs):
new_contact = Contact(*args, **kwargs)
self.append(new_contact)
def display_contacts(self):
for contact in self:
print(contact)
contacts = AddressBook()
contacts.add_contact("Adam Smith", "adam#email.com")
contacts.add_contact("Bob Jones", "bob#email.com")
contacts.add_contact("Charlie Doe", "charlie#email.com")
contacts.display_contacts()
class Contact(object):
contact_list = []
def __init__(self, name, email):
self.name = name
self.email = email
Contact.contact_list.append(self)
#classmethod
def print_contacts(cls):
for contact in cls.contact_list:
print(contact.name)
cont1 = Contact("John", "john#john.com")
cont2 = Contact("Mary", "mary#mary.com")
Contact.print_contacts()
will print
>>John
Mary
To answer your question as to why your code currently doesn't work: first, your init method doesn't need a return call, init is called upon object creation to establish object variables and typically doesn't need to return anything (especially in this case since .append() doesn't provide anything to return). Second, a classmethod seems better suited for what you are trying to do with the second method, and you can read more about that here: What are Class methods in Python for?
I have the following nested dict:
world = {'europe' :
{'france' : ['paris', 'lion'],
'uk' : ['london', 'manchester']}},
{'asia' :
{'china' : ['beijing'],
{'japan' : ['tokyo']}}
I'm trying the following objects out of it:
class world:
continents = {} # dict of continents
class continent(world):
name = '' # name of the continent
countries = {} # dict of countries
class country(continent):
name = '' # name of the country
cities = [] # list of cities
class city(country):
name = '' # name of the city
The goal is to get all countries from the continent object and alternatively to get the country and the continent names from a city object.
What is the best way to do so in Python?
Inheriting from "higher" classes is incorrect here. If you inherit a class, that inheriting class is the parent class plus more. You're saying here that country is a continent and also is a world. That is clearly not true and unnecessary.
There is no necessary hierarchical relationship between those four classes. Worlds are worlds, continents are continents, countries are countries and cities are cities. It's enough for continents to contain a list of the countries they hold, or conversely for a country to hold a reference to the continent it's in. The classes themselves do not need a hierarchical relationship.
Consider also whether such a strict 1:1 relationship is useful. There are countries which exist on more than one continent, depending on how exactly you want to define these terms (colonies are fun). How to design this data structure really depends on the concrete goal you have for it.
Syntactically, the classes should be defined as
class World(object):
def __init__(self, continents):
self.continents = continents
class Continent(World):
def __init__(self, name):
self.name = '' # name of the continent
...
class Country(Continent):
def __init__(self, name):
self.name = '' # name of the country
...
class City(Country):
def __init__(self, name):
self.name = '' # name of the city
...
However, in this case it does not make any sense.
Subclassing means something else:
Class Animal(object):
pass
Class Dog(Animal):
pass
Class Snake(Animal):
pass
A dog is a specific type of animal. A dog is an animal. A snake is also an animal.
In your case, a Continent is not a type of World, a Country is not a type of Continent and so on.
Instead you want to relate those classes, which can live as separate classes or they can go one inside the other.
For example
class City(object):
def __init__(self, name):
self.name = '' # name of the city
class Country(object, cities):
def __init__(self, name, cities):
self.name = name # name of the country
self.cities = cities # it's a list of Cities instances
class Continent(object):
def __init__(self, name, countries):
self.name = name # name of the continent
self.countries = countries # it's a list of Countries instances
class World(object):
def __init__(self, continents):
self.continents = continents # it's a list of Continent instances
france = Country('France', [City('Paris'), City('Annecy'), City('St. Tropez')])
italy = Country('Italy', [City('Rome'), City('Milan')])
uk = Country('UK', [City('London'), City('Bath')])
europe = Continent('europe', [france, italy, uk])
...
Note: the above is just an example. It may not be the best way to do it in python for a number of reasons, depending on how you intend to manipulate the objects.
It's a wide and long subject.
I suggest to look online for a good tutorial about Object Orientation (also called OOP for Object Oriented Programming or OOD for Object Oriented Design).
Here is one tutorial, but there are thousands available online.
After that, you will be able to design the interfaces your objects should expose in order to offer a certain functionality at the local/application level.
Tip: using a RDBM (Relational Data Base Management System), would help you relating and managing the models. Learn about ERD's (Entity-Relationship Diagrams) to help you design your data model.
:)
I am creating a phone book with python and was stumped on how to search through the class for a specific contact or how to search for a specific entry in the class.
This is what I have so far:
class person:
def __init__(self, first_name, last_name, phone_number):
person.first = first_name
person_last = last_name
person_number = phone_number
class friend:
def __init__(self, email, birth_date):
email = johnny.seagraves8219
birth_date = 8/13/1993
super(friend, self)._init_
ans = True
while ans:
print("""
1. Add a contact
2. Look up contact by name
Press enter to quit
""")
ans = input("What would you like to do?")
if ans == "1":
elif ans == "2":
look_up = input("Who would you like to look up?")
The class will not have entries that you can search through as far as I know. The class is basically just a constructor which is used to create an instance of a, in this case, person in the phone book. You could use an array to hold the instances and then search the array.
for i in arrayName:
if(arrayName[i].first == look_up):
# do something
To create an instance simply call the constructor:
firstPerson = person("Mike", "Ryans", "1800838699")
arrayName.append(firstPerson)
My knowledge of python is fairly limited but this is what I think should work.
Good luck!
To begin with, Flexicon is correct: your person/friend class is just a single entity that holds only a single persons information. To be able to search for people, you'll either need to make an array (list) of person objects, a map (dictionary) mapping a name or nickname to the object (so {'Timmie': <my_timidger_object>}, or you can wrap one of these approaches in a AddressBook class that contains additional methods that one of those basic data structures cannot do for you.
Some other important problems: your friend class does not extend the person, the constructor for a class has two underscores, like __init__; as well, you should add default values for email and birth_date in the person class, or trying to access these later will cause an error; your attribute need self before them or they will not be treated as attribute for the object
Here is Object Orientated (using a list to hold the people) way to do it, though it might be overkill for something this simple:
class person:
def __init__(self, first_name, last_name, phone_number):
self.person.first = first_name
self.person_last = last_name
self.person_number = phone_number
self.email = None #Notice the placeholders?
self.birth_date #Not having this information should not be exceptional
class friend(person): #Here, friend extends person
def __init__(self, email, birth_date):
self.email = email
self.birth_date = birth_date
super(friend, self).__init__()
class AddressBook:
def __init__(self, people = None):
if people:
self.entries = list(people)
else:
self.entries = []
#This is merely an example method, a better way would be to use some relational method like SQL to put in a query to find specific information about the person, but that is beyond the scope of this answer
def find_num(self, first_name, last_name):
for person in self.entries:
if (person.last_name, person.first_name) == (last_name, first_name):
return person
return None