I've been trying to practice with classes in Python, and I've found some areas that have confused me. The main area is in the way that lists work, particularly in relation to inheritance.
Here is my Code.
class LibraryItem:
def __init__(self, book_id, name):
self.item_id = book_id
self.name = name
class Library(LibraryItem):
books=[]
def all_books(books):
bookslen = len(books)
return bookslen
def add_book(books):
books.append(self.book_id)
What I'm trying to get the program to do is allow me to store an instance of a book_id and name, which can then be added to the list books[], via the add_books method in the child class Library.
I don't think inheritance is what your looking for here
You have i library and this library contains a list of books. keep it simple.
class Library:
## creates the library
def __init__(self):
self.books = []
## returns number of books
def number_of_books(self):
return len(self.books)
## adds a book to the list of books
def add_book(self, book):
self.books.append(book)
class Book:
## creates your book
def __init__(self, item_id, name):
self.item_id = item_id
self.name = name
localLibrary = Library() # create the library
new_book = Book(1,"Harry Potter") # create the book
localLibrary.add_book(new_book) # add the book to the library
print(localLibrary.number_of_books()) # display the number of books
## output -> 1 book in the library
I think this is what you trying to achieve
class LibraryItem:
def __init__(self, book_id, name):
self.item_id = book_id
self.name = name
class Library:
def __init__(self):
self.books = []
def __len__(self):
return len(self.books)
def add_book(self, book):
self.books.append(book.book_id)
now you can create instance of book and add it to library:
book1 = LibraryItem(1,"To Kill a Mockingbird")
library = Library()
library.add_book(book1)
print(len(library ))
Related
I know that each instance will inherit that attribute, but I want a function or should I call it a method of that class to return the set of all instances created of that class.
So let's say I created 3 instances and call a method from the last one that will return all the previously created instances as well as the one that I am calling it from.
I was able to achieve it by making a list, but would it be possible to return a set?
Is there some kind of constructor that I am missing for it?
class Bee():
instances = []
def __init__(self, name, identifier):
self.name = name
self.identifier = identifier
def __str__(self):
self.instances.append(f"{self.identifier} {self.name}")
return f"{self.identifier} {self.name}"
def get_hive(self):
return self.instances
Normally you would create Hive as a separate class and put the Bees inside. You then have a clear and explicit data structure whose job includes keeping track of all Bees created.
Something like:
class Hive:
def __init__(self):
self.bees = []
def add_bee(self, bee):
self.bees.append(bee)
class Bee:
def __init__(self, name, identifier):
self.name = name
self.identifier = identifier
def __str__(self):
return f"Bee({self.name}, {self.identifier})"
def __repr__(self):
return str(self)
# User code example
hive = Hive()
b1 = Bee('My Bee', 0)
b2 = Bee('Some Other Bee', 1)
hive.add_bee(b1)
hive.add_bee(b2)
print(hive.bees) # display all bees inside the hive
I have a use case, where I have to override one attribute in base class init, but the operations after that ( by making use of that attribute ) remains the same.
class Person:
def __init__(self, name, phone, record_file = None):
self.name = name
self.phone = phone
if self.record_file:
self.contents = json.load(open(self.record_file))
else:
self.contents = {'person_specific_details': details}
#### Do some operations with self.contents
class Teenager(Person):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# If self.record_file is None:
# self.contents = new for Teenager
self.contents = {'teenager_specific_details': teenager_details}
# But further operations remains the same (#### Do some operations with self.contents)
t = Teenager(phone='xxxxxx', name='XXXXXXX')
I am not able to acheive it properly. Can anyone help?
Your main problem is that you want to change an intermediate value in the Person.__init__, which won't work. But you could create an optional argument for the contents and just use that instead of the default one.
Like this:
class Person:
def __init__(self, name, phone, record_file=None, contents=None):
self.name = name
self.phone = phone
if record_file:
with open(record_file) as fp:
self.contents = json.load(fp)
else:
if contents: # can be utilized by other subclasses
self.contents = contents
else:
self.contents = {"person_specific_details": details}
#### Do some operations with self.contents
class Teenager(Person):
def __init__(self, **kwargs):
contents = {"teenager_specific_details": teenager_details}
super().__init__(contents=contents, **kwargs)
t = Teenager(phone="xxxxxx", name="XXXXXXX")
This way you can pass the Teenager specific contents to the base initializaion, and it can proceed further with that one.
I have 2 classes that are a composition. And I would need to extend the functionality of both, so I would need to make use of inheritance from those base classes. Is it possible?
class Book:
def __init__(self):
self.sheets = generate_sheets()
author = ''
def generate_sheets(self):
for index in range(20):
(some_extra code)
self.sheets.append(Sheet(index))
class Sheet:
def __init__(self, idx):
self.id = idx
And I want to create 2 children of the base, and that the composition be between them. It would only possible overwriting the method generate_sheets?
class DefinitionBook(Book):
def __init__(self):
super().__init__()
translator = ''
def generate_sheets(self):
super().__init__()
for index in range(20):
self.sheets.append(DefSheet(index)
class DefSheet(Sheet):
def __init__
super().__init__()
definition = dict()
Or it would be possible to keep part of the code of the method generate_sheets in the child class of Book, and only change the part where is called to the class Sheet/DefSheet?
You could have the class Sheet (and subclasses) you need to use, as a class variable in the class Book (and subclasses); this voids the need to override generate_sheets in the subclasses.
class Sheet:
def __init__(self, idx):
self.id = idx
def __str__(self):
return f"{self.__class__.__name__}"
class Book:
sheet = Sheet
def __init__(self):
self.sheets = []
self.generate_sheets()
def generate_sheets(self):
for index in range(20):
self.sheets.append(self.__class__.sheet(index))
def __str__(self):
return f"{self.__class__.__name__} - {self.sheet.__name__}"
class DefSheet(Sheet):
def __init__(self, idx):
super().__init__(idx)
class DefinitionBook(Book):
sheet = DefSheet
def __init__(self):
super().__init__()
b = Book()
print(b, b.sheets[0])
d = DefinitionBook()
print(d, d.sheets[0])
output:
Book - Sheet Sheet
DefinitionBook - DefSheet DefSheet
I have a class Participant as follows:
class Participant:
def __init__(self,name,level):
self.name = name
self.level =level
I also have another class Team as follows:
class Team:
def __init__(self,name):
self.name = name
I want to create a method in the class Team to add an instance of class Participant; for instance:
Assume myTeam is an empty, valid instance of Team.
Also assume myParticipant1 is a valid instances of Participant
myTeam.addParticipant(participant= myParticipant1)
AddParticipant method should add the instance myParticipant1 to the instance myTeam.
How do I acheive it in Python?
Aside from the inheritance questions we're talking about in the comments, this is pretty simple stuff.
class Participant(object):
def __init__(self, name, level):
self.name = name
self.level = level
class Team(object):
def __init__(self, name):
self.name = name
self.participants = []
def add_participant(p):
self.participants.append(p)
DEMO:
my_team = Team("Monty Python")
p_info = [("Adam", 10e5), ("Joe-bob", -1)]
participants = [Participant(name, level) for name, level in p_info]
for participant in participants:
my_team.add_participant(participant)
# say that 10 times fast....
In [1]: [p.name for p in my_team.participants]
Out[1]: ["Adam", "Joe-bob"]
Say I have a pair of instances that reference one another mutually. Is there a preferable manner to structure this relationship than the following.
class Human():
def __init__(self, name):
self.name = name
self.pet = Dog('Sparky', self)
def pet(self, animal):
self.pet.receive_petting()
class Dog(Pet):
def __init__(self, name, owner):
self.name = name
self.owner = owner
def receive_petting(self):
pass
def bark_at(self, person):
"do something"
The thing I don't like is that the relationship needs to be specified in two places. Any ideas on how to make this dryer?
I would break this into three classes:
class Human():
def __init__(self, name):
self.name = name
class Dog(Pet):
def __init__(self, name):
self.name = name
def bark_at(self, person):
"do something"
class OwnerPetRelation():
def __init__(self, dog, human):
self.owner=human
self.pet=dog
Now, one owner can also have many dogs, we just need to define as many OwnerPetRelations.
Similarly, a dog can also belong to multiple owners now.
I would create a method on Human that allows you to add pets (since a human might have many pets):
class Human():
def __init__(self, name):
self.name = name
self.pets = []
def add_pet(self, pet):
pet.owner = self
self.pets.append(pet)
def pet(self, animal):
for pet in self.pets:
pet.receive_petting()
class Dog(Pet):
def __init__(self, name):
self.name = name
self.owner = None
def receive_petting(self):
pass
def bark_at(self, person):
"do something"
This can be used as follows
human = Human('Jim')
human.add_pet(Dog('Woof'))
This approach can of course also be used for just a single pet and one could also extend it to allow pets to be owned by many humans.
There's nothing really Python-specific here; this is just a limitation of constructor-based dependency injection. It's hard to inject a reference to another object that cannot have been created yet. Instead, you can create an object that has a reference to something that will have a reference to the other object. For instance, you can pass a function to the constructor that will be able to return the value:
class Human():
def __init__(self,name,dog):
self.name = name
self._dog = dog
#property
def dog(self):
return self._dog()
class Dog():
def __init__(self,name,human):
self.name = name
self._human = human
#property
def human(self):
return self._human()
Then you can use it like this:
human = None
dog = Dog('fido',lambda: human)
human = Human('john',lambda: dog)
print(dog.human.name)
print(human.dog.name)
john
fido
It is not hard to update this so that the property function caches the value, of course. E.g.:
class Dog():
def __init__(self,name,human):
self.name = name
self._human = human
#property
def human(self):
try:
return self._human_
except AttributeError:
self._human_ = self._human()
return self._human_