Attribute error: can't set attribute for pygame Sprite inheritance

Question:

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:UsersHpDocumentsWorkingPersonalplatformer1game_modelswindowsplatformer_window.py", line 127, in load_level
    hp_bar = HPBar(
  File "C:UsersHpDocumentsWorkingPersonalplatformer1game_modelsspriteshp_bar.py", line 16, in __init__
    super().__init__(
  File "C:UsersHpDocumentsWorkingPersonalplatformer1contribmodelswidgetsprogress_barsprogress_bar.py", line 24, in __init__
    super().__init__(*args, **kwargs)
  File "C:UsersHpDocumentsWorkingPersonalplatformer1contribmodelswidgetswidget.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))
Asked By: Jobo Fernandez

||

Answers:

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
Answered By: Rabbid76
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.