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
Related
I want to update the class Square by adding the public getter and setter size
The setter should assign (in this order) the width and the height - with the same value
The setter should have the same value validation as the Rectangle for width and height
No need to change the exception error message (It should be the one from width)
The class Square inherits from Rectangle.
The setter for width in Rectangle:
def width(self):
"""
getter for #__width
"""
return self.__width
#width.setter
def width(self, width):
"""
validate and set the width attribute
"""
if width <= 0:
raise ValueError("width must be > 0")
if not isinstance(width, int):
raise TypeError("width must be an integer")
self.__width = width```
And what i tried to do in ```Square``` for the size attribute:
``` def __init__(self, size, x=0, y=0, id=None):
"""
Initialise a square with same width and height
"""
self.__size = size
super().__init__(self.size, self.size, x, y, id)
#property
def size(self):
"""
Getter for size
"""
return self.__size
#models.rectangle.Rectangle.width.setter
def size(self, size):
"""
Setter for size, using the logic for Rectangle's width
"""
self.__size = size
When I try to assign a value to size I get this error:
AttributeError: can't set attribute
Square is a Rectangle with the same width and height
In Python everything is public, so you can do something like this:
class Rectangle:
def __init__(self, width):
self.__width = width
#property
def width(self):
return self.__width
#width.setter
def width(self, width):
self.setter(Rectangle.__name__, width=width)
def setter(self, class_name, **kwargs):
for k, v in kwargs.items():
if v <= 0:
raise ValueError(f"{k} must be > 0")
if not isinstance(v, int):
raise TypeError(f"{k} must be an integer")
self.__setattr__(f"_{class_name}__" + k, v)
class Square(Rectangle):
def __init__(self, size, x=0, y=0, id=None):
self.__size = size
super().__init__(self.__size)
#property
def size(self):
return self.__size
#size.setter
def size(self, size):
self.setter(Square.__name__, size=size)
s = Square(1)
print("After __init__")
print(s.__dict__)
s.width = 5
s.size = 3
print("After setter")
print(s.__dict__)
print(s.size, s.width)
Output:
After __init__
{'_Square__size': 1, '_Rectangle__width': 1}
After setter
{'_Square__size': 3, '_Rectangle__width': 5}
3 5
I am trying to make a button class for my game, using pygame. But in the button class, I cannot call methods that are contained in the class itself.
I am new to classes so I may be missing something important, I'm sorry
I tried to add self to the isHovering() Method but it still doesn't work.
import pygame
class Button():
def __init__(self, pos, value, img, screen):
self.pos = pos
self.value = value
self.img = img
self.screen = screen
### Getters and Setters ###===============
### Get/Set Value : True/False ###
def setValue(self, value):
self.value = value
def getValue(self):
return self.value
### Get/Set Pos ###
def setPos(self, pos):
self.pos = pos
def getPos(self):
return self.pos
### Get/Set Img ###
def setImg(self, img):
self.img = img
def getImg(self):
return self.img
#==========================================
def isHovering(self):
pos = getPos()
imgRect = pygame.rect(pos[0], pos[1], 105, 105)
if imgRect.collidepoint(pygame.mouse.get_pos()):
return True
else:
return False
def update(self, screen):
if isHovering():
image = pygame.transform.scale(self.img(95, 95))
else:
image = pygame.transform.scale(self.img(105, 105))
screen.blit(image, self.pos)
I thought that when update(screen) was called in my main loop, that it would call isHovering(), and return a True or False, but instead I get this error:
NameError: name 'isHovering' is not defined
In def update(self, screen),
The if statement should be if self.isHovering().
If not, the interpreter will look for a isHovering function somewhere in the current module, like if you had defined it outside your class.
Using the self. prefix will indicate that you are indeed trying to call a method of the instance as JacoblRR already pointed out in a comment.
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")
I'm getting an issue with class inheritance in Python that I can't seem to make sense of. It may just be my inexperience with Python.
I was able to replicate the issue with this example (I'm using Python 3.3.4):
class R(object):
def __init__(self, x, y, w, h):
self._R = [x, y, w, h]
#property
def x(self):
return self._R[0]
#x.setter
def x(self, value):
self._R[0] = value
#property
def y(self):
return self._R[1]
#y.setter
def y(self, value):
self._R[1] = value
#property
def width(self):
return self._R[2]
#width.setter
def width(self, value):
self._R[2] = value
#property
def height(self):
return self._R[3]
#height.setter
def height(self, value):
self._R[3] = value
class Base(object):
def __init__(self):
self.pos = (0, 0)
class A(Base):
def __init__(self):
Base.__init__(self)
self.rect = R(0, 0, 0, 0)
#property
def pos(self):
return (self.rect.x, self.rect.y)
#pos.setter
def pos(self, value):
(self.rect.x, self.rect.y) = value
class B(A):
def __init__(self):
A.__init__(self)
self.foo = "bar"
o = B()
o.pos = (50, 50)
which produces the following error:
Traceback (most recent call last):
File "main.py", line 52, in <module>
o = B()
File "main.py", line 49, in __init__
A.__init__(self)
File "main.py", line 37, in __init__
Base.__init__(self)
File "main.py", line 33, in __init__
self.pos = (0, 0)
File "main.py", line 45, in pos
(self.rect.x, self.rect.y) = value
AttributeError: 'B' object has no attribute 'rect'
You are trying to use the self.pos setter before self.rect is set:
class Base(object):
def __init__(self):
self.pos = (0, 0)
class A(Base):
def __init__(self):
Base.__init__(self)
self.rect = R(0, 0, 0, 0)
Because self.pos on A is a property, trying to set self.pos requires self.rect to already be set.
move the Base.__init__(self) call down:
class A(Base):
def __init__(self):
self.rect = R(0, 0, 0, 0)
Base.__init__(self)
Now self.rect is set by the time Base.__init__ tries to assign a value to self.pos.
i think if you change it to this
class A(Base):
def __init__(self):
self.rect = R(0, 0, 0, 0)
Base.__init__(self)
it will work
the problem is that Base.__init__ sets pos , which is a setter method of B that references self.rect but self.rect is not created until after the init call.
so to resolve the issue, simple ensure that self.rect exists before calling __init__ constructor
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).