Here's the coding problem I am trying to solve... I have a base class, let's say Animal, and it has two subclasses, say Dog and Cat. My class Animal has a method, make_baby(), that both Dog and Cat will inherit. The trick I'm having trouble pulling off is that I want the return value to be a new instance of the subclass that called the function but with different attribute values, i.e. Dog.make_baby() should return a new Dog and Cat.make_baby() will return a new Cat.
I previously tried returning "type(self)()", but this is no good because type() return a type object, not a class.
Here is the full example code:
Class Animal():
def __init__(self, color):
self.color = color
def make_baby():
new_color = rand_color # a randomly chosen color
return #??? new class of the same type that called the method
Class Dog(Animal):
def pet():
print '*pant*'
Class Cat(Animal):
def pet():
print 'purrr'
So I'd like to avoid writing a make_baby() method for Dogs and Cats because the idea is that the method is exactly the same except for the returned class. I'd also like to avoid a bunch of if statements because I'd like to make and arbitrarily large number of subclasses to Animal.
You wrote:
this is no good because type() return a type object, not a class.
A type is a class, if you're using new-style classes. If you're using Python 3, you're set; all Python 3 classes are "new-style." If you're using Python 2.x, derive your class from object (or from something else that derives from object, like any built-in Python type).
But what you really want here is a class method, where you get a reference to the class passed in automatically.
class Animal(object):
def __init__(self, color):
self.color = color
#classmethod
def make_baby(cls):
return cls(rand_color) # randomly-chosen color
You can call it on the class (e.g. Animal.make_baby() or Dog.make_baby()) or on an instance; either way the method still receives the class as the first argument.
type() can be used to construct entirely new classes. What you want is:
class Animal():
def __init__(self, color):
self.color = color
def make_baby(self):
new_color = rand_color # a randomly chosen color
return self.__class__(new_color)
You approach will totally work! Just use new style classes.
Class Animal(object):
def __init__(self, color):
self.color = color
def make_baby(self):
new_color = rand_color # a randomly chosen color
return type(self)(new_color)
Class Dog(Animal):
def pet():
print '*pant*'
Class Cat(Animal):
def pet():
print 'purrr'
However, if make_baby(self) is not relying on details of self, what you want is a class-wide factory method, like in #Kindall's answer.
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
Here is my code - my base_file.py
class modify_file(object):
def modify_file_delete_obj():
print "modify file here"
def modify_file_add_attributes():
print "modify file here"
return ["data"]
class task_list(object):
modify_file_instance = modify_file() #problem part when accessing from project1.py
def check_topology():
data = modify_file_instance.modify_file_add_attributes()
#use this data further in this method
def check_particles():
print "check for particles"
project1.py file
import base_file as base_file
class project1(base_file.modify_file,base_file.task_list):
#overriding method of modify_file class
def modify_file_add_attributes(self):
print "different attributes to modify"
return ["different data"]
The idea is to run base_file.py for most projects and the project specific ones when required.
But when i run the method
"check_topology" from project1.py
the modify_file class is being derived from the base_file.py not project1.py
So the output is still ["data"] not ["different data"]
If you want to correctly use inheritance, define a base class Pet which provides a method to be overridden by a specific kind of pet.
class Pet(object):
def talk(self):
pass
class Cat(Pet):
def talk(self):
return "meow"
class Dog(Pet):
def talk(self):
return "woof"
pets = [Cat(), Dog(), Cat()]
for p in pets:
print(p.talk())
# Outputs
# meow
# woof
# meow
(I leave the issue of what Pet.talk should do, if anything, as a topic for another question.)
You are mixing up object composition with multiple inheritance.
The task_list class uses object composition when it creates an internal instance of the modify_file class. But there is a problem here in that you are creating it as a class attribute, which means it will be shared by all instances of task_list. It should instead be an instance attribute that is created in an __init__ method:
class task_list(object):
def __init__(self):
super(task_list, self).__init__()
self.modify_file_instance = modify_file()
def check_topology(self):
data = self.modify_file_instance.modify_file_add_attributes()
The project1 class uses multiple inheritance, when in fact it should use single inheritance. It is a kind of task_list, so it makes no sense for it to inherit modify_file as well. Instead, it should create it's own internal sub-class of modify_file - i.e. use object composition, just like task_list class does:
# custom modify_file sub-class to override methods
class project1_modify_file(base_file.modify_file):
def modify_file_add_attributes(self):
print "different attributes to modify"
return ["different data"]
class project1(base_file.task_list):
def __init__(self):
super(project1, self).__init__()
self.modify_file_instance = project1_modify_file()
Now you have a consistent interface. So when project1.check_topology() is called, it will in turn call task_list.check_topology() (by inheritance), which then accessses self.modify_file_instance (by composition):
>>> p = project1()
>>> p.check_topology()
different attributes to modify
In your dog class you're re-constructing an instance of cat, this instance (and the cat type) does not know they are inherited elsewhere by pets.
So you can naturally try:
class cat(object):
def meow(self):
self.sound = "meow"
return self.sound
class dog(object):
def woof(self):
return self.meow()
class pets(cat,dog):
def meow(self):
self.sound = "meow meow"
return self.sound
print(pets().woof())
Which still make no sense with those actual names, but you told they are fake names so it make be OK.
I'm relatively new to Python and have problems with immutable variables.
I'm trying to change the value of a class attribute (e.g. car.color). The difficulty is, that I can not use the namespace of car for doing this.
Up to now I did not find a satisvying answer to my questions. In the code below I tried to summarize the possible solutions (workarrounds) I found and their disadvantages:
class Car:
def __init__(self):
self.color = "green"
self.color_list = ["green"]
self.color_attrib = "green"
self.name = "VW Golf"
"""
and many more attributes...
"""
def makesetter(self, attribute):
def set_value(value):
attribute=value
return set_value
def set_color(self, value):
"in this function I directly have access to car.color and can change its value: "
self.color = value
def set_attrib(self, attribute_string, value):
setattr(self,attribute_string,value)
def change_attribute(attribute, value):
"In this function I can not access car.color directly"
attribute=value
def change_attribute_list(attribute, value):
"In this function I can not access car.color directly"
attribute[0] = value
if __name__ == "__main__":
car1 = Car()
change_attribute(car1.color, "red")
print(car1.color) # Color does not change because car1.color is immutable
g = car1.makesetter(car1.color)
g("red")
print(car1.color) # Color does not change because car1.color is immutable
change_attribute_list(car1.color_list, "red")
print(car1.color_list) # Color changes but seems like a workarround
# Disadvantage: in the namespace of car1, the user has to use a list to access a string value "car1.color_list[0]"
car1.set_color("red")
print(car1.color) # Color changes but seems like a workarround
# Disadvantage: Car needs a setter function for each attribute
car1.set_attrib("color_attrib","red")
print(car1.color_attrib) # Color changes but seems like a workarround
# Disadvantage: Attribute has to be passed as string & no auto completion while coding
Actually the function setattr() is internally exactly doing what I want. But it works with a string argument.
I tried to look into this function but it seems to be written in C++.
So do I have to use C++ to solve this problem without a workarround?
Or is there a Pythionic way of doing this?
The problem is you are trying to redefine the value of an instance from outside of the class. Since in __init__ you are defining your variables with self, they are only available for that instance. This is the point of a class - it's what makes them extensible and reusable.
Ideally, you would make a method within the class that would update those attributes, however, if you really need to update the class from an external function, you will have to define it as a class level variable. For instance:
class Car:
def __init__(self):
Car.color = "green"
can now be updated using:
def change_attribute(attribute, value):
"In this function I can not access car.color directly"
Car.color=value
outside of the class because you have not assigned it to one specific instance. Doing this presents a problem, however. Since we don't have a separate instance variable, if we try to re-instantiate the class, we are stuck with what was previously changed, i.e.
if name == "main":
car1 = Car()
car2 = Car()
change_attribute(car1.color, "red")
print(car1.color) # Prints red
print(car2.color) # Prints red
change_attribute(car2.color, "blue")
print(car1.color) # Prints blue
print(car2.color) # Prints blue
This is why classes themselves should be self contained and are meant to be immutable - the instance itself should be changed.
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