I want an attribute of a child class to have a different name than the same attribute of its parent class even though it means the same thing. For example, a parent class is Shape with attribute "height" and child class Circle with similar arttribute "Diameter". Below is a simplification of what I current have but I want the Circle class to use "diameter" instead of "height". What is the best way to handle this?
NOTE: I will inherit from Circle in another class that also needs to use "diameter" instead of "height". Thank you!
class Shape():
def __init__(self, shape, bar_args, height):
self.shape = shape
self.height = height
etc.
class Circle(Shape):
def __init__(self, height, foo_args, shape='circle'):
Shape.__init__(self, shape, height)
self.height = height
etc.
You could define a property which accesses the original attribute on read and write access:
class Circle(Shape):
def __init__(self, height, foo_args, shape='circle'):
Shape.__init__(self, shape, height) # assigns the attributes there
# other assignments
#property
def diameter(self):
"""The diameter property maps everything to the height attribute."""
return self.height
#diameter.setter
def diameter(self, new_value):
self.height = new_value
# deleter is not needed, as we don't want to delete this.
If you want this behaviour very often and you find property handling with setter and getter too unhandy, you can go a step higher and build your own descriptor class:
class AttrMap(object):
def __init__(self, name):
self.name = name
def __get__(self, obj, typ):
# Read access to obj's attribute.
if obj is None:
# access to class -> return descriptor object.
return self
return getattr(obj, self.name)
def __set__(self, obj, value):
return setattr(obj, self.name, value)
def __delete__(self, obj):
return delattr(obj, self.name)
With this, you can then do
class Circle(Shape):
diameter = AttrMap('height')
def __init__(self, height, foo_args, shape='circle'):
Shape.__init__(self, shape, height) # assigns the attributes there
# other assignments
and the diameter descriptor will redirect all accesses to it to the named attribute (here: height).
Related
I have a working code with an HPBar class --> inherits from ProgressBar class --> inherits from pygame.sprite.Sprite. I decided to create a Widget class to have the following inheritance flow: HPBar --> ProgressBar --> Widget --> pygame.sprite.Sprite. The point in doing so is for flexibility especially when adding more widgets like buttons, textboxes, etc. However, in my revision I encountered Attribute error: can't set attribute. Details are as follows.
Somewhere in my code I have this HPBar instantiation:
hp_bar = HPBar(
x=x, y=y,
entity=self.player,
groups=[self.camera, self.extras],
)
Working Code:
This worked prior to the revision.
class HPBar(ProgressBar):
def __init__(self, entity, *args, **kwargs):
max_value = entity.stats["max_hp"]
value = entity.stats["hp"]
super().__init__(
max_value=max_value, value=value,
width=32, height=5,
*args, **kwargs
)
class ProgressBar(pygame.sprite.Sprite):
def __init__(
self,
x: float,
y: float,
width: int,
height: int,
groups: List[pygame.sprite.AbstractGroup] = [],
max_value: int,
value: int,
*args, **kwargs
):
super().__init__(groups)
#property
def image(self):
_image = # pygame surface
return _image
Revised Code:
The code with the Attribute error.
class ProgressBar(Widget):
def __init__(
self,
max_value: int,
value: int,
*args, **kwargs
):
super().__init__(*args, **kwargs)
#property
def image(self):
_image = # pygame surface
return _image
class Widget(pygame.sprite.Sprite):
def __init__(
self,
x: float, y: float,
width: int, height: int,
groups: List[pygame.sprite.AbstractGroup] = [],
):
super().__init__(groups)
self.image = pygame.Surface((width, height))
Traceback Error:
File "C:\Users\Hp\Documents\Working\Personal\platformer1\game_models\windows\platformer_window.py", line 127, in load_level
hp_bar = HPBar(
File "C:\Users\Hp\Documents\Working\Personal\platformer1\game_models\sprites\hp_bar.py", line 16, in __init__
super().__init__(
File "C:\Users\Hp\Documents\Working\Personal\platformer1\contrib\models\widgets\progress_bars\progress_bar.py", line 24, in __init__
super().__init__(*args, **kwargs)
File "C:\Users\Hp\Documents\Working\Personal\platformer1\contrib\models\widgets\widget.py", line 22, in __init__
self.image = pygame.Surface((width, height))
AttributeError: can't set attribute
Few debugging attempts:
I tried to print out the width and height arguments inside the Widget class to make sure I'm receiving and sending the correct data type:
In Widget class:
super().__init__(groups)
print(width, height)
print(type(width), type(height))
self.image = pygame.Surface((width, height))
Print result:
32 5
<class 'int'> <class 'int'>
Moreover, I have had this implementation resembling my Widget class implementation and this works fine:
class Player(pygame.sprite.Sprite):
def __init__(self, pos):
super().__init__()
self.image = pygame.Surface((16, 32))
Yes, of course. You can't have a method/property and an attribute with the same name. image can be either an attribute or a property. But you can't have 2 objects with the same name.
The following is not possible:
class Foo:
def __init__(self):
self.bar = 1
def bar(self):
return 2
print(Foo().bar())
print(Foo().bar())
TypeError: 'int' object is not callable
Also not possible:
class Foo:
def __init__(self):
self.bar = 1
#property
def bar(self):
return 2
print(Foo().bar)
self.bar = 1
AttributeError: can't set attribute 'bar'
However you can define a setter:
class Foo:
def __init__(self):
self._bar = 1
self.bar = 2
#property
def bar(self):
return self._bar
#bar.setter
def bar(self, value):
self._bar = value
print(Foo().bar)
2
Hey my task is to create a parentclass Shape and pass the functions on to the different shapes. Since a Circlearea calculation takes different parameters I am trying to overwrite the function. It throws following warning: "Signature of method 'Circel.get_area()' does not match Signature in the base method in Shape". Is that possible at all? How would I have to do it? Should it work anyway later on?
class Shape(ABC):
#abstractmethod
def get_area(self, x_l, y_l):
pass
def move(self, newx, newy):
pass
class Circle(Shape):
def __init__(self, rad, x_pos, y_pos):
self.rad = rad
self.pos = (x_pos, y_pos)
self.area = self.get_area(self, self.rad)
def get_area(self, rad):
return(self.rad*self.rad*2*m.pi)
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
...
class Wolf:
def __init__(self, legs):
self.legs = 4
class Dog(Wolf):
def __init__(self, color):
self.color = color
fido = Dog(legs = 4, color = "brown")
This would spute out an error message. How would I do something like that where I add parameters to the subclass that doesn't pertain to the superclass.
Try this:
class Wolf:
def __init__(self, legs):
self.legs = 4
class Dog(Wolf):
def __init__(self, legs, color):
super().__init__(legs)
self.color = color
fido = Dog(legs=4, color="brown")
That's not how inheritance works. When you inherit from another class, the super-class's parameters are not automatically added to the sub-class's parameter list. You must explicitly accept the desired parameters in your sub-class's constructor and pass them on to the super class's constructor:
class Wolf:
def __init__(self, legs):
self.legs = 4
class Dog(Wolf):
def __init__(self, color, legs):
super().__init__(legs)
self.color = color
fido = Dog(legs = 4, color = "brown")
Here's an example from a tutorial which explains inheritance and shows how to do this. You need to call the parent class's init function as in this similar example from this tutorial:
class Pet(object):
def __init__(self, name, species):
self.name = name
self.species = species
def getName(self):
return self.name
def getSpecies(self):
return self.species
def __str__(self):
return "%s is a %s" % (self.name, self.species)
class Dog(Pet):
def __init__(self, name, chases_cats):
Pet.__init__(self, name, "Dog")
self.chases_cats = chases_cats
def chasesCats(self):
return self.chases_cats
You still have to pass in the legs argument for Dog, and then use super:
class Wolf:
def __init__(self, legs):
self.legs = 4
class Dog(Wolf):
def __init__(self, color, legs):
super().__init__(legs)
self.color = color
fido = Dog(legs = 4, color = "brown")
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