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)
Related
Let's say I create a class "Animal" and make a child class "Cow", is it possible to make a method in "Animal" that I can call from the class "Cow" and that will generate a class of itself?
For example:
class Animal:
def __init__(self, identity, food, water):
self.identity = identity
self.food = food
self.water = water
def baby(self):
# make an object of the child class
class Cow(Animal):
pass
new_cow = Cow("id" + str(randint(100000, 999999)), 100, 10)
new_cow.baby() # make a new cow object
I don't even know how to start with the baby() method, but I hope you guys understand my question
def baby(self):
return type(self)()
type(self) gets you the class object of the current instance, e.g. Cow, and () makes a new instance of it, e.g. same as Cow(). Of course, Cow() requires three arguments, so either you have to provide them when you call baby, or otherwise pass them. E.g.:
def baby(self, *args):
return type(self)(*args)
...
new_cow.baby(42, 100, 10)
Or:
def baby(self):
return type(self)(self.identity + '1', self.food, self.water)
I have a class which contains a list like so:
class Zoo:
def __init__(self):
self._animals = []
I populate the list of animals with animal objects that have various properties:
class Animal:
def __init__(self, speed, height, length):
self._speed = speed
self._height = height
self._length = length
You can imagine subclasses of Animal that have other properties. I want to be able to write methods that perform the same calculation but on different attributes of the Animal. For example, an average. I could write the following in Zoo:
def get_average(self, propertyname):
return sum(getattr(x, propertyname) for x in self.animals) / len(self.animals)
That string lookup not only messes with my ability to document nicely, but using getattr seems odd (and maybe I'm just nervous passing strings around?). If this is good standard practice, that's fine. Creating get_average_speed(), get_average_height(), and get_average_length() methods, especially as I add more properties, seems unwise, too.
I realize I am trying to encapsulate a one-liner in this example, but is there a better way to go about creating methods like this based on properties of the objects in the Zoo's list? I've looked a little bit at factory functions, so when I understand them better, I think I could write something like this:
all_properties = ['speed', 'height', 'length']
for p in all_properties:
Zoo.make_average_function(p)
And then any instance of Zoo will have methods called get_average_speed(), get_average_height(), and get_average_length(), ideally with nice docstrings. Taking it one step further, I'd really like the Animal objects themselves to tell my Zoo what properties can be turned into get_average() methods. Going to the very end, let's say I subclass Animal and would like it to indicate it creates a new average method: (the following is pseudo-code, I don't know if decorators can be used like this)
class Tiger(Animal):
def __init__(self, tail_length):
self._tail_length = tail_length
#Zoo.make_average_function
#property
def tail_length(self):
return self._tail_length
Then, upon adding a Tiger to a Zoo, my method that adds animals to Zoo object would know to create a get_average_tail_length() method for that instance of the Zoo. Instead of having to keep a list of what average methods I need to make, the Animal-type objects indicate what things can be averaged.
Is there a nice way to get this sort of method generation? Or is there another approach besides getattr() to say "do some computation/work on an a particular property of every member in this list"?
Try this:
import functools
class Zoo:
def __init__(self):
self._animals = []
#classmethod
def make_average_function(cls, func):
setattr(cls, "get_average_{}".format(func.__name__), functools.partialmethod(cls.get_average, propertyname=func.__name__))
return func
def get_average(self, propertyname):
return sum(getattr(x, propertyname) for x in self._animals) / len(self._animals)
class Animal:
def __init__(self, speed, height, length):
self._speed = speed
self._height = height
self._length = length
class Tiger(Animal):
def __init__(self, tail_length):
self._tail_length = tail_length
#property
#Zoo.make_average_function
def tail_length(self):
return self._tail_length
my_zoo = Zoo()
my_zoo._animals.append(Tiger(10))
my_zoo._animals.append(Tiger(1))
my_zoo._animals.append(Tiger(13))
print(my_zoo.get_average_tail_length())
Note: If there are different zoos have different types of animals, it will lead to confusion.
Example
class Bird(Animal):
def __init__(self, speed):
self._speed = speed
#property
#Zoo.make_average_function
def speed(self):
return self._speed
my_zoo2 = Zoo()
my_zoo2._animals.append(Bird(13))
print(my_zoo2.get_average_speed()) # ok
print(my_zoo.get_average_speed()) # wrong
print(my_zoo2.get_average_tail_length()) # wrong
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.
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
Im having some trouble understanding Inheritance in classes and wondering why this bit of python code is not working, can anyone walk me through what is going wrong here?
## Animal is-a object
class Animal(object):
def __init__(self, name, sound):
self.implimented = False
self.name = name
self.sound = sound
def speak(self):
if self.implimented == True:
print "Sound: ", self.sound
def animal_name(self):
if self.implimented == True:
print "Name: ", self.name
## Dog is-a Animal
class Dog(Animal):
def __init__(self):
self.implimented = True
name = "Dog"
sound = "Woof"
mark = Dog(Animal)
mark.animal_name()
mark.speak()
This is the output through the terminal
Traceback (most recent call last):
File "/private/var/folders/nd/4r8kqczj19j1yk8n59f1pmp80000gn/T/Cleanup At Startup/ex41-376235301.968.py", line 26, in <module>
mark = Dog(Animal)
TypeError: __init__() takes exactly 1 argument (2 given)
logout
I was trying to get animal to check if an animal was implemented, and then if so, get the classes inheriting from animal to set the variables that Animals would then be able to manipulate.
katrielalex answered your question pretty well, but I'd also like to point out that your classes are somewhat poorly - if not incorrectly - coded. There seems to be few misunderstandings about the way you use classes.
First, I would recommend reading the Python docs to get the basic idea: http://docs.python.org/2/tutorial/classes.html
To create a class, you simply do
class Animal:
def __init__(self, name, sound): # class constructor
self.name = name
self.sound = sound
And now you can create name objects by calling a1 = Animal("Leo The Lion", "Rawr") or so.
To inherit a class, you do:
# Define superclass (Animal) already in the class definition
class Dog(Animal):
# Subclasses can take additional parameters, such as age
def __init__(self, age):
# Use super class' (Animal's) __init__ method to initialize name and sound
# You don't define them separately in the Dog section
super(Dog, self).__init__("Dog", "Woof")
# Normally define attributes that don't belong to super class
self.age = age
And now you can create a simple Dog object by saying d1 = Dog(18) and you don't need to use d1 = Dog(Animal), you already told the class that it's superclass is Animal at the first line class Dog(Animal):
To create an instance of a class you do
mark = Dog()
not mark = Dog(Animal).
Don't do this implimented stuff. If you want a class that you can't instantiate (i.e. you have to subclass first), do
import abc
class Animal(object):
__metaclass__ = abc.ABCMeta
def speak(self):
...
Since age in the given example is not part of the parent (or base) class, you have to implement the the function (which in a class is called method) in the class which inheritted (also known as derived class).
class Dog(Animal):
# Subclasses can take additional parameters, such as age
def __init__(self, age):
... # Implementation can be found in reaction before this one
def give_age( self ):
print self.age