python multiple inheritance: avoiding attribute naming conflict - python

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?

Related

How can I make my functions to be part of Class?

I want to make Books/Customer/Loan Classes and make all my function to be part of the class, but instead I only make it worse (I think lol).
I know that my code needs a lot of correction (Make the code more readable) but still maybe some Hero here could help and save my day (Make me learn something new along the way)
P.S: I'm learning how to make readable code so be gentle LOL.
Example of Customer Class of my code:
class Customer:
"""
A class that represents the Customer object
"""
def __init__(self, customer_id, customer_name, customer_city, customer_age):
"""
A function that contains all the relevant information of customers
:param customer_id: Customer's ID
:param customer_name: Customer's name
:param customer_city: Customer's city of living
:param customer_age: Customer's age'
"""
self.customer_id = customer_id
self.customer_name = customer_name
self.customer_city = customer_city
self.customer_age = customer_age
def __str__(self):
return f"{self.customer_id},{self.customer_name},{self.customer_city},{self.customer_age}"
Example of function that I want to make it part of the Customer Class instead of using it as a regular Function:
def add_new_customer(customer_id, customer_name, customer_city, customer_age):
"""
A function that add new customer to the Library
:param customer_id: Customer's ID'
:param customer_name: Customer's name'
:param customer_city: Customer's city'
:param customer_age: Customer's age'
"""
# todo: try different method that can return dict. return customers_library["Customers"].append({"Customer's ID":customer_id,"Customer's Name":customer_name,"Customer's City":customer_city,"Customer's age":customer_age})
new_customer = customers_library["Customers"].append(
{"Customer's ID": customer_id, "Customer's Name": customer_name, "Customer's City": customer_city,
"Customer's age": customer_age})
with open('customers_data.pkl', 'wb') as customer_save:
pickle.dump(customers_library, customer_save)
return new_customer
First of all, add_new_customer shouldn't be part of Customer class. I would rather see it as a method of Library which could contains collection of all customers. But to make it class method you just need to put it inside class, remember about identation and instance of the class (self) as first parameter.
Another hint and good practice is to not duplicate names - instead of customer.customer_name just write customer.name. In add_customer function you already now you adding customer, so it can take just name, city and age.
id is an exception as it would shadow builting id function and it's really common in database so in that one case it's good to have customer_id.
Another hint, you could remove a lot code with dataclasses
class Customer:
"""
A class that represents the Customer object
"""
from dataclasses import dataclass
#dataclass
class Customer
customer_id: int
name: str
city: str
age: str
And if we go further, there are possibility to put alias on field with dataclass-json or with pydantic, look:
from pydantic import BaseModel, Field
class Customer(BaseModel):
customer_id: int = Field(alias="Customer's ID")
class Config:
allow_population_by_field_name = True
c = Customer(customer_id=10)
print(c.dict(by_alias=True)) # prints {"Customer's ID": 10}
Which will simplify customer adding a lot (actually you can pickle pydantic model directly but it's one more way to go).

Accessing derived attributes in a named tuple from parent class

I have a simple named tuple which functions as a immutable container that characterize a class I designed.
From this Class (that has the namedtuple as class variable), I dervied multiple childclasses that all override this attribute and add new fields to the named tuple
I want to keep the fields that were defined in the Parent class ID and only add the ones that are new. I assume you could simply keep the old ID in the class and do something like ID.2nd = "added form child"
But I'd much prefere if you could simply override the ID variable and acess the previously defined ID's via a call to super() or something
from collections import namedtuple
ID = namedtuple("myTuple", ["first", "second", "third", "fourth"])
ID.__new__.__defaults__ = ("default from parentclass", None , None , None)
class Parent(object):
_ID = ID()
def __init__(self, arg):
self.arg = arg
class Child(Parent):
#if there is a new _ID defined in Child
#, keep the fields it adds but also add the
#ones from the parent:
_ID = ID(second="adding from child")
#it should now contain the fields _ID.first == "default from parent" from parentclass
#and _ID.second == "adding from child"
So far this works, but if I have another child now
class Child2(Child):
_ID = ID(third="adding from child")
#and now something like _ID.second = super()._ID.second
I will lose all information that was added in the intermediate classes.

Python class structure and inheritage

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!

Create a inherited class with user input

I'm trying to code a product inventory project. I stumbled across this problem and have been searching since a few days but couldn't find any way to do it. How can I create a class inherited from the base class with user input..
Here is the base class:
class Product:
def __init__(self, title, color, price):
self.title = title
self.color = color
self.price = price
what I'm trying to achive is that user will be able to add products himself by lets say pressing 2 will ask him to enter the product type (coat) and than the parameters specific to the product which will create a new class inherited from the base class. Like this:
class coats(Product):
def __init__(self, material, size):
self.material = material
self.size = size
I can create instances with user input but creating inherited classes I couldn't figure it out. I appreciate if you could help. Thanks.
While you can generate classes dynamically with the type() function, you really do not want to do this here. Code generation to model user-generated data only leads to very, very difficult to manage code that looks up attributes and variable names dynamically.
Use a datastructure in your existing class to store user-generated data. All you need is a dictionary:
class Product:
def __init__(self, type, price, properties):
self.type = type
self.price = price
self.properties = properties
then to create a coat product use:
products = {}
products['coat'] = Product('coat', 10.0, {'color': 'some color', 'material': 'some material', size: 12})
where the various values, and the key in products are not literals but variables with user input.
Classes model your application, and contain the data. Don't create separate models for the data.

How to search within a class from a menu or add to a class from menu-python

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

Categories

Resources