Is it possible to subclass in Python using an already instantiated superclass?
I don't exactly know how to frame the question so let me give an example. Suppose I have a class Rectangle and I want to build another class ColoredRectangle. But I don't want each ColoredRectangle of the same dimensions to be its own new Rectangle. So when I initiate ColoredRectangle I would pass it an already instantiated Rectangle.
Ie., I want
r = Rectangle([1,2])
r_red = ColoredRectangle(r, "red")
r_blue = ColoredRectangle(r, "blue")
But now r_red and r_blue should be able to get all rectangle methods and attributes. For example suppose Rectangle had an area() attribute.
r.area
2
r_red.area
2
r_red and r_blue should "point" to the same Rectangle. I know I could do this by writing:
class ColoredRectangle(Rectangle):
def __init__(self, rectangle, color):
self.color = color
self.rectangle = rectangle
But then I'd have to write
r_red.rectangle.area
which is ugly.
Inheritance
Inheritance is such a nice thing in python, and I don't think you have to resort to getattr hacks, if you want those, scroll down.
You can force the class dictionary to refer to another object:
class Rectangle(object):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
class ColoredRectangle(Rectangle):
def __init__(self, rect, color):
self.__dict__ = rect.__dict__
self.color = color
rect = Rectangle(3, 5)
crect = ColoredRectangle(rect, color="blue")
print crect.width, crect.height, crect.color
#3 5 blue
These two will refer to the same Rectangle object:
crect.width=10
print rect.width, rect.height
#10 5
This is an exellent talk on metaprogramming, and while it's title implies Python3 a lot of it also applies to python 2.x: David Beazley - Python3 Metaprogramming
getattr hacking
If for any reason however, you would want to have multiple ColoredRectangle refer to the same base Rectangle then these will conflict with each other:
eve = Rectangle(3, 5)
kain = ColoredRectangle(eve, color="blue")
abel = ColoredRectangle(eve, color="red")
print eve.color, kain.color, abel.color
#red red red
If you'd like different "proxy objects", which can get attributes from the base Rectangle but not interfere with each other, you have to resort to getattr hacking, which is fun too:
class ColoredRectangle(Rectangle):
def __init__(self, rect, color):
self.rect = rect
self.color = color
def __getattr__(self,attr):
return getattr(self.rect,attr)
eve = Rectangle(3, 5)
This will avoid the interference:
kain = ColoredRectangle(eve, color="blue")
abel = ColoredRectangle(eve, color="red")
print kain.color, abel.color
#blue red
About __getattr__ versus __getattribute__:
A key difference between getattr and getattribute is that
getattr is only invoked if the attribute wasn't found the usual ways. It's good for implementing a fallback for missing attributes,
and is probably the one of two you want. source
Because only non found attributes will be handled by __getattr__ you can also partially update your proxies, which might be confusing:
kain.width=10
print eve.area(), kain.area(), abel.area()
# 15 50 15
To avoid this you can override __setattr__:
def __setattr__(self, attr, value):
if attr == "color":
return super(ColoredRectangle,self).setattr(attr,value)
raise YourFavoriteException
What you seem to be asking to do is to redirect attribute accesses to the underlying Rectangle object. The __getattr__ method can do this for you.
class ColoredRectangle(object):
def __init__(self, rectangle, color):
self.color = color
self.rectangle = rectangle
def __getattr__(self,attr):
return getattr(self.rectangle,attr)
Owerwrite all attributes by rectangle attributes. They are in __dict__ property.
import copy
class Rectangle(object):
def __init__(self, area):
self.area = area
class ColoredRectangle(Rectangle):
def __init__(self, rectangle, color):
self.__dict__ = copy.deepcopy(rectangle.__dict__)
self.color = color
Related
# Set up the screen
import turtle
class Screen(turtle.Screen()):
# Create and define an object
def __init__(self, title, bgcolor, height, width, tracer = 0):
turtle.Screen().__init__(self)
# Assign attribute to our instance
self.title = title
self.bgcolor = bgcolor
self.height = height
self.width = width
self.tracer = tracer
Your issue is that you are trying to inherit from a class instance rather than from a class. Note that when you are doing
class Screen(turtle.Screen()):
you are creating a new instance of the turtle._Screen class.
To fix this error, you just have to inherit from the right class as such:
class Screen(turtle._Screen):
# Create and define an object
def __init__(self, title, bgcolor, height, width, tracer = 0):
# Note that here I use super() to access the parent class instead of creating a new instance with turtle.Screen()
super().__init__(self)
# Assign attribute to our instance
self.title = title
self.bgcolor = bgcolor
self.height = height
self.width = width
self.tracer = tracer
Hope this helps you
I'm from NON computer science background (Biochemist). In Python programming under "Create a class function", I didn't get the output for the following written function:
class circle (object):
def __init__(self, 10, 'red'):
self.radius=10;
self.color='red'
I got syntax error like this:
File "<ipython-input-1-ab699251caa9>", line 2
def_init_(self,10,'red'):
^
SyntaxError: invalid syntax
you cant pass pure value you need to pass it with variables
class circle (object):
def __init__(self,number=10,color='red'):
self.radius=number
self.color=color
Function parameters must be variables, not numbers or strings. Change this:
class circle (object):
def __init__(self, 10, 'red'):
self.radius = 10
self.color = 'red'
to this:
class circle (object):
def __init__(self, radius, color):
self.radius = radius
self.color = color
This class can be instantiated with:
myCircleObject = circle(10, 'red')
or, if you want to make the association parameter-value more clear, you can instantiate with:
myCircleObject = circle(radius = 10, color = 'red')
You can also set default values for the parameters:
class circle (object):
def __init__(self, radius = 10, color = 'red'):
self.radius = radius
self.color = color
In this way, you can still instantiate the object as before but you can also init with:
myCircleObject = circle()
In this case, radius and color will have the default values specified in the signature (radius will be 10 and the color will be 'red').
Note that to avoid misinterpretations, parameters with default values can only be listed in the rightmost part. So if you want to specify a default value only for the parameter radius, you need to move it to the right:
class circle (object):
def __init__(self, color, radius = 10):
self.radius = radius
self.color = color
Suppose I have a parent class and multiple child classes, which extend the parent class by including more specific information about the ideas they represent. For example:
class Shape:
def __init__(self, center):
self.center = center
class Square(Shape):
def __init__(self, center, side_length):
super().__init__(self, center)
self.side_length = side_length
self.area = side_length ** 2
class Circle(Shape):
def __init__(self, center, radius):
super().__init__(self, center)
self.radius = radius
self.area = 3.14 * (radius ** 2)
Suppose I want to implement a method such as translate(new_center) in the parent class, which would return a new object with a different center location than the original object. Because all child classes should behave the same way (i.e. the attribute self.center should change), it makes sense to implement translate() as a method of the parent class Shape.
If I want to return a new object of type Shape every time translate() is called, we can simply define translate() as a method of Shape like so:
def translate(self, new_center):
return Shape(new_center)
However, if any child class instances call this method, the result will be of type Shape, and thus any additional state information contained by the original instance, such as side_length and area for a Square, will be lost. Additionally, translate() cannot be defined as
def translate(self, new_center):
return self.__class__(new_center)
because the constructors for each of the child classes require additional arguments that the parent class constructor doesn't. How can I implement this without having to override the parent method in each of the child classes (avoiding which was the entire point of defining the parent method)?
You could copy the object and modify the copy:
import copy
class Shape():
def __init__(self, center):
self.center = center
def translate(self, new_center):
new_shape = copy.copy(self) # Replace with deepcopy if needed
new_shape.center = new_center
...
Im attempting to call the grandparent method getColor() from the Class below.
This is the grandparent class in its own file:
class IsVisible(object):
def __init__(self):
pass
white = (255,255,255)
black = (0,0,0)
red = (255,0,0)
blue = (0,0,255)
green = (0,255,0)
def getColor(self):
return self.color
This is the parent class and child class.
from IsVisible import *
class Object(IsVisible):
def __init__(self):
super(Object, self).__init__()
self.posSize = []
def getPosSize(self):
return self.posSize
class Rectangle(Object):
def __init__(self):
super(Rectangle, self).__init__()
self.color = self.blue
self.posSize = [20,50,100,100]
So Im trying to call getColor by creating an object
rectangle = Rectangle()
and then calling
rectangle.getColor()
However I'm getting an error. Namely:
AttributeError: 'Rectangle' object has no attribute 'getColor'
Now I have no idea how to solve this. I have tried to google "python call grandparent method" but I only get instructions for how to call a method that is overrun... I believe I have stated the inheritance correctly so I don't know what the problem is. Could anyone help me?
If it's just indentation (and nothing else is jumping out at me) —
class IsVisible(object):
def __init__(self):
pass
white = (255,255,255)
black = (0,0,0)
red = (255,0,0)
blue = (0,0,255)
green = (0,255,0)
def getColor(self): # <--- indent this
return self.color
But be careful about class vs. instance variables. See this answer for more (not copied here since it's not the answer to your question above).
Also, please don't use Object as the name of a class :) . It is too likely to lead to confusion with the real object, or to confuse the reader about what you mean. How about ColoredShape instead of IsVisible, PositionedShape instead of Object, and Rectangle(PositionedShape)?
Is it possible to inherit a class and use its init function without declaring all parameters again in the child class?
I have a class with lots of parameters, but I don't want to use a list (**args). I wouldn't see my actual parameters:
class Table(object):
def __init_(self, name, height ...):
self.name = name
self.height = height
class RoundTable(Table):
def __init__(self, radius):
self.radius = radius
table = RoundTable(name = "Placeholder",
height = 10,
radius = 20)
Use the super class before assigning specfic args
def __init__(self,radius,*args,**kwargs):
super().__init__(*args,**kwargs)
self.radius = radius
Edit : I'm assuming you mean class and not a function