Two constructors in a Python Class - python

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

Related

Python: pass a class-function as a reference to an external function

I have a class with several functions.
From outside that class, I want to specify with a reference which function to call - but I'm not sure how.
For example, I have an Animal class with two functions sound and food. I want to write a Zoo class which receives one of the Animal's functions as input and applies that function to every animal instance it holds (the function all_animals_features).
class Animal:
def __init__(self, sound, food):
self.my_sound = sound
self.my_food = food
def sound(self):
# Do some complicated stuff....
return self.my_sound
def food(self):
return self.my_food
class Zoo():
def __init__(self, animals):
self.animals = animals
def all_animals_features(self, f):
return [animal.f() for animal in self.animals]
dog = Animal('Woof', 'Bone')
cat = Animal('Meow', 'Cream')
zoo = Zoo([cat, dog])
zoo.all_animals_features(Animal.sound)
But of course, 'Animal' object has no attribute 'f'.
Any idea how can this be implemented?
Clarification: if all that is needed, as demonstrated by this silly example, is just getting an attribute then it may be simpler to use getattr().
In your case you just need to adjust the way the method is called:
class Zoo():
def __init__(self, animals):
self.animals = animals
def all_animals_features(self, f):
return [f(animal) for animal in self.animals]
dog = Animal('Woof', 'Bone')
cat = Animal('Meow', 'Cream')
zoo = Zoo([cat, dog])
print(zoo.all_animals_features(Animal.sound))
Output:
['Meow', 'Woof']
Since you supply Animal.sound, as parameter f, the call in the list comprehension is: f(animal)

Search for object in array by parameter in Python

I used created an array of objects belonging to a class. Each object has values that are unique to themselves. I need to find class a parameter's value belongs to.
For example
class Dog:
def __init__(self, name):
self.name = name
dogNames = ["Rex", "Otis", "Max"]
dogs = []
for dog in dogNames:
dogs.append(Dog(dog))
>>> dogs
[<__main__.Dog instance at 0x1181c5cf8>, <__main__.Dog instance at 0x117c7a050>, <__main__.Dog instance at 0x117d169e0>]
I would need to find which object has the name "Rex" assigned to it.
What is the most Pythonic way of doing this ?
if the names are unique, don't create a list, create a dictionary:
dog_names = ["Rex", "Otis", "Max"]
dogs = {name:Dog(name) for name in dog_names}
now you can perform fast dict lookup with default value to None if name not found:
rex_object = dogs.get("Rex")
if you want to keep your list, the same thing can be achieved (but with O(n) complexity with a generator expression and next defaulting to None if no dog is called "Rex":
next((d for d in dogs if d.name=="Rex"),None)
the most pythonicway is to use repr dunder method in your Dog class
class Dog:
def __init__(self,name):
self.name = name
def __repr__(self):
return 'dog name is {}'.format(self.name)
A simple way to do it is through the existing for loop that you have and just throw in an if statement to check if the name equals the name you're looking for and assign it to a variable.
find = ""
for dog in dogNames:
if dog.name == "Rex":
find = dog

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.

Prepopulating instances in code for classes (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.

using django, how do i construct a proxy object instance from a superclass object instance?

I am still a bit confused about the relation of Proxy models to their Superclasses in django. My question now is how do I get a instance of a Proxy model from an already retrieved instance of the Superclass?
So, lets say I have:
class Animal(models.Model):
type = models.CharField(max_length=20)
name = models.CharField(max_length=40)
class Dog(Animal):
class Meta:
proxy = True
def make_noise(self):
print "Woof Woof"
Class Cat(Animal):
class Meta:
proxy = True
def make_noise(self):
print "Meow Meow"
animals = Animal.objects.all()
for animal in animals:
if (animal.type == "cat"):
animal_proxy = # make me a cat
elif (animal.type == "dog"):
animal_proxy = # make me a dog
animal_proxy.make_noise()
OK. So.. What goes into "# make me a cat" that doesn't require a query back to the database such as:
animal_proxy = Cat.objects.get(id=animal.id)
Is there a simple way to create an instance of Cat from an instance of Animal that I know is a cat?
You are trying to implement persistence for an inheritance hierarchy. Using one concrete table and a type switch is a nice way to do this. However I think that your implementation, specifically:
for animal in animals:
if (animal.type == "cat"):
animal_proxy = # make me a cat
is going against the grain of Django. The switching on type shouldn't be extraneous to the proxy (or model) class.
If I were you, I'd do the following:
First, add a "type aware" manager to the proxy models. This will ensure that Dog.objects will always fetch Animal instances with type="dog" and Cat.objects will fetch Animal instances with type="cat".
class TypeAwareManager(models.Manager):
def __init__(self, type, *args, **kwargs):
super(TypeAwareManager, self).__init__(*args, **kwargs)
self.type = type
def get_query_set(self):
return super(TypeAwareManager, self).get_query_set().filter(
type = self.type)
class Dog(Animal):
objects = TypeAwareManager('dog')
...
class Cat(Animal):
objects = TypeAwareManager('cat')
...
Second, fetch subclass instances separately. You can then combine them before operating on them. I've used itertools.chain to combine two Querysets.
from itertools import chain
q1 = Cat.objects.all() # [<Cat: Daisy [cat]>]
q2 = Dog.objects.all() # [<Dog: Bruno [dog]>]
for each in chain(q1, q2):
each.make_noise()
# Meow Meow
# Woof Woof
I would do:
def reklass_model(model_instance, model_subklass):
fields = model_instance._meta.get_all_field_names()
kwargs = {}
for field_name in fields:
try:
kwargs[field_name] = getattr(model_instance, field_name)
except ValueError as e:
#needed for ManyToManyField for not already saved instances
pass
return model_subklass(**kwargs)
animals = Animal.objects.all()
for animal in animals:
if (animal.type == "cat"):
animal_proxy = reklass_model(animal, Cat)
elif (animal.type == "dog"):
animal_proxy = reklass_model(animal, Cat)
animal_proxy.make_noise()
# Meow Meow
# Woof Woof
I have not tested it with "the zoo" ;) but with my own models seems to work.

Categories