1

I was wondering if there was a way to generate class attributes by looping over the arguments of the init method without explicitly referring to a list containing all the arguments of the init method?

In the example below could I loop over hp, image, speed, x, y to create the self arguments ?

class Character(pygame.sprite.Sprite):
    def __init__(self, hp, image, speed, x, y):

        # Call the parent class (Sprite) constructor
        super(Character, self).__init__()

        self.image = image
        self.rect = self.image.get_rect().move(x, y) #initial placement
        self.speed = speed
        self.hp = hp

For example with a loop that would look like that:

class Character(pygame.sprite.Sprite):
    def __init__(self, hp, image, speed, x, y):

        # Call the parent class (Sprite) constructor
        super(Character, self).__init__()

        for arg in arguments:
             self.arg = arg

I am not quite sure how to get the "arguments" to refer to hp, image, speed, x and y ? Or am I stuck with using a list like below ?

class Character(pygame.sprite.Sprite):
    def __init__(self, hp, image, speed, x, y):

        # Call the parent class (Sprite) constructor
        super(Character, self).__init__()

        for arg in [self, hp, image, speed, x, y]:
             self.arg = arg
user2390182
  • 72,016
  • 6
  • 67
  • 89
Sorade
  • 915
  • 1
  • 14
  • 29
  • `for arg in [self, hp, image, speed, x, y]: self.arg = arg` will just leave you with a single attribute `arg` with value of `y` .... – user2390182 Apr 29 '16 at 09:44
  • @Sorade was the answer usefull (if yes, please accept)? Did you solve your issue (Please let other people know how you did it)? – salomonderossi Apr 29 '16 at 10:34

2 Answers2

4

You can use keyword arguments (kwargs) and define a list of attributes your instances require and you therefore expect in your __init__(). Then you can loop over them and assign your attributes via setattr:

class Character(pygame.sprite.Sprite):
    ATTRS = ('hp', 'image', 'speed', 'x', 'y')

    def __init__(self, **kwargs):
        # Call the parent class (Sprite) constructor
        super(Character, self).__init__()
        for attr in self.ATTRS:
            setattr(self, attr, kwargs.get(attr))  # sets to None if missing
        set_rect(...)  # do your processing of x, y

Or, even simpler, just turning all kwargs into instance attributes:

class Character(pygame.sprite.Sprite):
    def __init__(self, **kwargs):
        super(Character, self).__init__()
        for key, value in kwargs.items():
            setattr(self, key, value)

I would, however, advise you against such trickery. It might make your __init__ shorter, but it will hurt your productivity later, as most IDE's (Eclipse-PyDev, PyCharm, etc.) code completion/resolution features will not detect such dynamically set attributes on existing instances and also not suggest the required arguments when calling the constructor, which is especially annoying for other coders using your class.

It also does not make your code more readable. Imagine inheriting a code base that uses lots of such constructions. You will learn to like a clean explicit version like the first one you are suggesting in your question. A compromise to shorten your constructor would be using multiple assignment

self.image, self.speed, self.hp = image, speed, hp
self.rect = self.image.get_rect().move(x, y)
Community
  • 1
  • 1
user2390182
  • 72,016
  • 6
  • 67
  • 89
-1

You can use a argument list, but I'm not sure if this is what you want...

class Test(object):
    def __init__(self, *args):
        for arg in args:
            print(arg)

t = Test("one", 2, "three")
salomonderossi
  • 2,180
  • 14
  • 20