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

Time:09-17

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))

Edit:

I got the code working by omitting the self.image attribute in Widget class. I'm assuming it's because of the image property in ProgressBar. However, I still don't fully understand why. Hopefully someone can explain.

class Widget(pygame.sprite.Sprite):
    def __init__(
            self,
            x: float, y: float,
            width: int, height: int,
            groups: List[pygame.sprite.AbstractGroup] = [],
    ):
        super().__init__(groups)

CodePudding user response:

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