Prepopulating instances in code for classes (Python) - python

I am trying to make it so that when I can run a function which would populate a class with a couple of instances which are contained within the code.
class Pets(object):
def __init__(self, name, scientific_name, feet_number, type)
super(Pets,self).__init__()
self.name = name
self.scientific_name = scientific_name
self.feet_number = feet_number
self.type = type
This is the point where I get stuck.
I want to make a function which has a list of instances (Ex. a dog, a cat, a horse...) so that when the function is run those instances can be accessed immediately.
I know from places like Creating dynamically named variables from user input (Second Paragraph, First Sentence), that what I'm asking for is possible, I just don't know the syntax for it.

Is this what you are trying to do?
class Pet(object):
def __init__(self, details):
self.name = details[0]
self.scientific_name = details[1]
self.feet_number = details[2]
self.type = details[3]
if __name__ == '__main__':
pet_list = [('Cat', 'Kitty Cat', 4, 'Cuddly'), ('Dog', 'Puppy Wuppy', 3, 'Licky')]
pets = [Pet(item) for item in pet_list]
Which gives you:
pets
> [<__main__.Pet at 0x8134d30>, <__main__.Pet at 0x8134d68>]
pets[0]
> <__main__.Pet at 0x8134d30>
pets[0].name
> 'Cat'
pets[0].scientific_name
> 'Kitty Cat'
pets[1].name
> 'Dog'
There are a lot of ways this could be put together depending on what you want to do. For example, you could make a master class called Pet() with some basic attributes and methods that are true for all pets, then create specific classes for each pet that inherit the base class, e.g. class Cat(Pet):
Or you could give the Pet class the ability to know all the other details depending on what name is passed into it, then populate the instance variables accordingly.

Related

Add objects from my class to dictionaries and be able to get specific attributes from the objects in the dictionary

I want a method in my class that creates new objects from user-inputs and adds them to a dictionary (this may not be the best way of doing this, appreciate any input). Further I want to be able to still get the attributes of the objects in the dictionary for other functions in a simple way which to me is the hardest part.
I have looked around for any similar problems but cant seem to find any, while this issue seems pretty general to me I believe there is an easy way to do this or i'm just doing something very wrong. If i just add the objects to a dictionary I just the "position" of the object but I want the specific attributes added to the dictionary based on name and species as given below.
animals = {}
class animal:
def __init__(self,name,species):
self.name = name
self.species = species
def dict(self,name,species):
#i want this function to create and add new objects to animals
For a given user input of name and species I would like for the output of dict() to add the new object to a dictionary and then somehow for example being able to loop through the dictionary for the names of all animals in the dictionary. Thank you so much in advance.
I am not sure why you want to do this, but you can use a class method to effectively make a new constructor that also takes a dictionary and mutate that dictionary inside
class Animal:
def __init__(self, name, species):
self.name = name
self.species = species
#classmethod
def init_and_track(cls, dict_, name, species):
a = cls(name, species)
dict_[name] = a
and use as so
animals = {}
Animal.init_and_track(animals, 'sally', 'horse')
as for getting user inputs, you could instead do something like
#classmethod
def init_and_track(cls, dict_):
name = input('name: ')
species = input('species: ')
a = cls(name, species)
dict_[name] = a
where the name and species are made by the user upon calling this method. I am not sure exactly what you want and why you want it this way but this is what I believe you are asking for.
Your comment suggests your original question was an xy-problem - how about something like this. Define your animal using dataclasses because it is just a holder for some data (assuming python 3.7)
from dataclasses import dataclass
#dataclass
class Animal:
name: str
species: str
def say_hi(self):
print(f'{self.name} the {self.species} says hi')
the : <...> you see are type annotations. Then define a new class to keep track of these animals.
class Zoo:
def __init__(self):
self.animals = []
def add_animal(self):
print('adding a new animal to the zoo')
name = input('what is it\'s name? ')
species = input('what species is it? ')
a = Animal(name, species)
self.animals.append(a)
def find_by_name(self, name):
animals_with_name = [a for a in self.animals if a.name == name]
return animals_with_name
def find_by_species(self, species):
return [a for a in self.animals if a.species = species]
If you only wanted to keep track of one attribute like by species, you are right that a dictionary would be best. If you wanted to keep track of more attributes do something like
def find_by_attribute(self, attribute_name, value):
return [a for a in self.animals if getattr(a, attribute_name) == value]
to avoid writing duplicate code. This should help get you started

Change attribute in multiple objects

I'm learning OOP in Python and I get stucked with one thing.
I have an example class:
class Animal:
def __init__(self, name="", hunger=0):
self.name = name
self.hunger = hunger
def eat(self):
self.hunger += 1
And some objects:
dog = Animal("dog")
cat = Animal("cat")
giraffe = Animal("giraffe")
I would like to use method eat() to change value of hunger in every single one of them at one blow. I have already tried to do something like this:
Animal.eat()
But it doesn't work (there's TypeError, because of missing argument 'self').
Also:
Animal.hunger += 1
Doesn't work (returns AttributeError).
If anyone has any ideas, I would be very grateful!
You can maintain a class variable that collects the instances and adjust all of their hungers in eat:
class Animal:
instances = []
def __init__(self, name="", hunger=0):
self.name = name
self.hunger = hunger
Animal.instances.append(self)
def eat(self):
for i in Animal.instances:
i.hunger += 1
Semantically, you might want to make it a classmethod, though
#classmethod
def eat(cls):
for i in cls.instances:
i.hunger += 1
You can still call it on instances if you so wish.
#schwobaseggi has the most straightforward answer for what you want to do, but what you want to do seems like it's asking for trouble. You have one class that does two very different things. Animal is an animal that has a name and eats, and it also keeps track of every animal instance and makes all of them eat. Animal is trying to do what individual animals do and also control a group of animals.
It might be better to split this into two different kinds of objects: An animal, and some sort of AnimalGroup like Zoo or Farm or Herd. The AnimalGroup class should be responsible for keeping track of a bunch of instances and make them all do stuff.
class AnimalGroup(object):
def __init__(self, animal_list):
self.animals = animal_list[:]
def add_animal(self, animal):
self.animals.append(animal)
def all_eat(self):
for animal in self.animals:
animal.eat()
then
dog = Animal("dog")
cat = Animal("cat")
giraffe = Animal("giraffe")
group = AnimalGroup([dog, cat, giraffe])
group.all_eat()
group.add_animal(Animal("pig"))
group.all_eat()
This separates out the responsibilities of each class and makes things much easier to change later on. You can now have different group behaviors without ever needing to change the animal class. You can have new animal classes that inherit from Animal and you don't need to worry about side effects. for example: class Mammal(Animal) . When I call Mammal.eat, will it update all animals? It might. class variables can be a bit tricky like that. Should it update all animals? No idea. With an AnimalGroup object, you don't need to worry.
You actually have to call it on the object itself like this:
cat.eat()
dog.eat()
giraffe.eat()
otherwise it doesn't know which object to actually change. You could store all your Objects in an array and loop over that array to call the function on all of them one after another:
dog = Animal("dog")
cat = Animal("cat")
giraffe = Animal("giraffe")
animals=[dog, cat, giraffe]
for animalType in animals:
animalType.eat()
now you can do them all at once or one at a time if you want. You will however need to addnew animals to the array after you create them to keep the list up to date:
fish=new Animal("fish")
animals.append(fish)
class Animal(object):
hunger = 0
def __init__(self, name=""):
self.name = name
def eat(self):
Animal.hunger = Animal.hunger + 1
dog = Animal("dog")
cat = Animal("cat")
giraffe = Animal("giraffe")
dog.eat()
print("Dog's hunger variable is", dog.hunger)
1
dog.eat()
print("Dog's hunger variable is :",dog.hunger)
2
print("Cat's hunger variable is :",cat.hunger)
2
print("Giraffe's hunger variable is :", giraffe.hunger)
2
When eat() is called on a single instance, the hunger variable is updated for all instances!
If you're wanting to do something on the class you have to declare it as a class variable:
class Animal:
hunger = 0
def __init__(self, name=""):
self.name = name
#classmethod
def eat(klass):
klass.hunger += 1
This way anytime you call Animal.eat() you'll be referencing the class method that modifies your class variable. You can still access the variable from within an Animal class with self.hunger but I would advise against that as it can get confusing coming back and trying to determine what's a class variable and what's a member variable.
To the best of my knowledge (and I really like OOP in python), the only way to do this is to create a new class with that specific attribute a.k.a.
class Animals:
def __init__(self, animals):
self.animals = animals
def all_eat(self):
for animal in animals:
animal.eat()
Then what you would have to do is:
dog = Animal("dog")
cat = Animal("cat")
giraffe = Animal("giraffe")
animals = Animals((dog, cat, giraffe))
animals.all_eat()
The reason for this is that python classes themselves do not have callable attributes so you have to call each instance of the class separately.

Two constructors in a Python Class

Is it OK to have 2 constructor functions, the regular __init__ and a #classmethod Animal.get?
Since creating a new object is computationally intensive, we want to store previously created instances in a class attribute cls.zoo and get a cached copy of the instance from cls.zoo if it exists.
The user will not access Animal.zoo directly. If the user wants to get an Animal object, he will always use Animal.get().
Is this approach proper/pythonic?
I'm not familiar with the Singleton Pattern. Is the code considered using the Singleton Pattern?
class Animal:
zoo = {}
# will not be called directly from outside the class
def __init__(self, species ,age):
self.species = species
self.age = age
self.runExpensiveFunction()
# User alway use this function
#classmethod
def get(cls, species):
if species in cls.zoo:
animal = cls.zoo[species]
else:
animal = Animal(species, 0)
cls.zoo[species] = animal
return animal
tiger = Animal.get('tiger')
bear = Animal.get('bear')
It depends whether you just want to allow the user of your class to access a cached object, or if you want to force it to only access that cached object. With you solution, user can always use tiger2 = Animal('tiger', 0) to get another instance.
If you really want only one instance, you can use __new__:
class Animals(object):
zoo = {}
def runExpensiveFunction(self):
print "EXPENSIVE CALLED"
def __new__(cls, species):
if species in cls.zoo:
return cls.zoo[species]
else:
animal = object.__new__(Animals)
animal.species = species
animal.age = 0
animal.runExpensiveFunction()
Animals.zoo[species] = animal
return animal
Here is the proof that you can only create one instance:
>>> tiger1 = Animals('tiger')
EXPENSIVE CALLED
>>> tiger2 = Animals('tiger')
>>> tiger2 is tiger1
True

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)

How to effectively save referenced class type in Python? (appengine)

I'd like to refer to different classes in a persistent appengine Model. Like:
class Animal():
def eat(self, food):
return food - 1
class Cat(Animal):
def eat(self, food):
return food - 2
class Dog(Animal):
def eat(self, food):
return food - 3
class Person(db.Model):
name = db.StringProperty()
pet = Animal()
I want to be able to assign either a Cat() or Dog() instance as a pet, save it and be able to reload a Person object with the original type of animal referenced in it. Persistance for the pet objects are not important, I only want to define different sets of behaviours with the methods of the classes. How do I do this?
Thank you!
Check out the PolyModel class.
The best I can come up with myself is a getter method (assuming the above Animal, Cat, Dog classes):
class Person(db.Model):
name = StringProperty()
pet = StringProperty(choices=('cat','dog'))
def get_pet(self):
dict = { 'cat': Cat, 'dog': Dog }
return dict[self.pet]
This way I have to maintain the list of available pet types in two additional places (choices for the saved string variable and the dictionary of string-class pairs). Is there a more risk free and elegant way to do this?

Categories