How to Create Instance Attributes By Lopping Over __init__ Arguments? - python

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

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)

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

Related

Correct way of returning new class object (which could also be extended)

I am trying to find a good way for returning a (new) class object in class method that can be extended as well.
I have a class (classA) which has among other methods, a method that returns a new classA object after some processing
class classA:
def __init__(): ...
def methodX(self, **kwargs):
process data
return classA(new params)
Now, I am extending this class to another classB. I need methodX to do the same, but return classB this time, instead of classA
class classB(classA):
def __init__(self, params):
super().__init__(params)
self.newParams = XYZ
def methodX(self, **kwargs):
???
This may be something trivial but I simply cannot figure it out. In the end I dont want to rewrite the methodX each time the class gets extended.
Thank you for your time.
Use the __class__ attribute like this:
class A:
def __init__(self, **kwargs):
self.kwargs = kwargs
def methodX(self, **kwargs):
#do stuff with kwargs
return self.__class__(**kwargs)
def __repr__(self):
return f'{self.__class__}({self.kwargs})'
class B(A):
pass
a = A(foo='bar')
ax = a.methodX(gee='whiz')
b = B(yee='haw')
bx = b.methodX(cool='beans')
print(a)
print(ax)
print(b)
print(bx)
class classA:
def __init__(self, x):
self.x = x
def createNew(self, y):
t = type(self)
return t(y)
class classB(classA):
def __init__(self, params):
super().__init__(params)
a = classA(1)
newA = a.createNew(2)
b = classB(1)
newB = b.createNew(2)
print(type(newB))
# <class '__main__.classB'>
I want to propose what I think is the cleanest approach, albeit similar to existing answers. The problem feels like a good fit for a class method:
class A:
#classmethod
def method_x(cls, **kwargs):
return cls(<init params>)
Using the #classmethod decorator ensures that the first input (traditionally named cls) will refer to the Class to which the method belongs, rather than the instance.
(usually we call the first method input self and this refers to the instance to which the method belongs)
Because cls refers to A, rather than an instance of A, we can call cls() as we would call A().
However, in a class that inherits from A, cls will instead refer to the child class, as required:
class A:
def __init__(self, x):
self.x = x
#classmethod
def make_new(cls, **kwargs):
y = kwargs["y"]
return cls(y) # returns A(y) here
class B(A):
def __init__(self, x):
super().__init__(x)
self.z = 3 * x
inst = B(1).make_new(y=7)
print(inst.x, inst.z)
And now you can expect that print statement to produce 7 21.
That inst.z exists should confirm for you that the make_new call (which was only defined on A and inherited unaltered by B) has indeed made an instance of B.
However, there's something I must point out. Inheriting the unaltered make_new method only works because the __init__ method on B has the same call signature as the method on A. If this weren't the case then the call to cls might have had to be altered.
This can be circumvented by allowing **kwargs on the __init__ method and passing generic **kwargs into cls() in the parent class:
class A:
def __init__(self, **kwargs):
self.x = kwargs["x"]
#classmethod
def make_new(cls, **kwargs):
return cls(**kwargs)
class B(A):
def __init__(self, x, w):
super().__init__(x=x)
self.w = w
inst = B(1,2).make_new(x="spam", w="spam")
print(inst.x, inst.w)
Here we were able to give B a different (more restrictive!) signature.
This illustrates a general principle, which is that parent classes will typically be more abstract/less specific than their children.
It follows that, if you want two classes that substantially share behaviour but which do quite specific different things, it will be better to create three classes: one rather abstract one that defines the behaviour-in-common, and two children that give you the specific behaviours you want.

Correct way of passing a self variable as argument to a mixin parent method

I have to model a warrior and the different kinds of attacks he can perform. The idea is to use mixins to contain the attack logic. I have my classes defined in the following way:
class Warrior:
def __init__(self, energy):
self.energy = energy
class TemplarKnight(Warrior, HandToHandCombatMixin):
pass
class CombatMixin:
def __init__(self):
self.attacks_cost = {}
def attack(self, attacker, attack_cost):
if attacker.energy < attack_cost:
print('Not enough energy to attack')
else:
attacker.energy -= attack_cost
print('Attack!')
class HandToHandCombatMixin(CombatMixin):
def __init__(self):
super().__init__()
self.attacks_cost['sword_spin'] = 10
def sword_spin(self, attacker):
return self.attack(attacker, self.attacks_cost['sword_spin'])
But the problem comes when I try to test this setup. When I do
class TestTemplarKnight(unittest.TestCase):
def setUp(self):
self.templar = TemplarKnight(energy=100)
def test_templar_knight_can_sword_spin(self):
self.templar.sword_spin(self.warrior)
self.assertEquals(self.templar.energy, 90)
I get
def sword_spin(self, attacker):
return self.attack(
> attacker, self.attacks_cost['sword_spin'])
E AttributeError: 'TemplarKnight' object has no attribute 'attacks_cost'
It seems that Python thinks that the parameter self.attacks_cost (when calling self.attack() inside the sword_spin() method of the HandToHandCombatMixin class) belongs to the TemplarKnight class instead of the HandToHandCombatMixin.
How should I have written this code to make Python look for self.attacks_cost inside HandToHandCombatMixin?
To use super correctly, all the classes involved need to use it. Right now, Warrior.__init__ is called first, but it doesn't use super, so HandToHandCombatMixin.__init__ is never called.
Make the following additions:
class Warrior:
def __init__(self, energy, **kwargs):
super().__init__(**kwargs)
self.energy = energy
class TemplarKnight(Warrior, HandToHandCombatMixin):
pass
class CombatMixin:
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.attacks_cost = {}
def attack(self, attacker, attack_cost):
if attacker.energy < attack_cost:
print('Not enough energy to attack')
else:
attacker.energy -= attack_cost
print('Attack!')
class HandToHandCombatMixin(CombatMixin):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.attacks_cost['sword_spin'] = 10
def sword_spin(self, attacker):
return self.attack(attacker, self.attacks_cost['sword_spin'])
Now when you instantiate TemplarKnight, you'll guarantee that all the __init__ methods are called, and in the correct order. Eventually, once of the calls to super() will cause object.__init__ to be called, at which point the chain finally ends. If you are correctly handling the keyword arguments, **kwargs will be empty by the time that happens.

Diamond inheritance in Python with different signatures

Here's the setup:
class Player(object):
def __init__(self, heigth):
self.heigth = heigth
print('do not forget that this should happen once!')
class Attacker(Player):
def __init__(self, heigth, goal_probability):
super().__init__(heigth)
self.goal_prob = goal_probability
def hit(self):
pass
# implementation
class Goalie(Player):
def __init__(self, heigth, save_probability=0.1):
super().__init__(heigth)
self.save_prob = save_probability
def catch(self):
pass
# implementation
class UniversalPlayer(Attacker, Goalie):
pass
up = UniversalPlayer(heigth=1.96, goal_probability=0.6)
It all works as expected: the MRO chooses Attacker first, then Goalie. I call UniversalPlayer's constructor with Attacker's __init__ signature, Goalie's constructor is called with Player's signature, it goes ok because save_probability has a default value but the problem is that I have no way of choosing save_probability, apart from setting up.save_probability after instantiating up, which I find very inelegant.
Furthermore, had Goalie not had a default value for save_probability, this code would raise an exception.
Is there a way to write UniversalPlayer so that I can choose save_probability too, or is there some fundamental problem here that cannot be worked around?
Each additional parameter to __init__ needs to have a class responsible for removing it from calls to super, so that when object.__init__ is finally called, you don't accidentally pass any arguments to it. Additionally, each method has to accept arbitrary arguments and pass them on for the next method to possibly handle.
# Player will be responsible for height
class Player(object):
def __init__(self, height, **kwargs):
super().__init__(**kwargs) # Player needs to use super too!
self.height = height
print('do not forget that this should happen once!')
# Attacker will be responsible for goal_probability
class Attacker(Player):
def __init__(self, height, goal_probability, **kwargs):
super().__init__(height, **kwargs)
self.goal_prob = goal_probability
def hit(self):
pass
# Goalie will be responsible for save_probability
class Goalie(Player):
def __init__(self, height, save_probability=0.1, **kwargs):
super().__init__(height, **kwargs)
self.save_prob = save_probability
def catch(self):
pass
# implementation
class UniversalPlayer(Attacker, Goalie):
pass
# Pass all arguments
# Life is easier if you stick to keyword arguments when using super().__init__
up = UniversalPlayer(height=1.96, goal_probability=0.6, save_probability=0.2)
Now, Attacker.__init__ is the first to be called. It uses goal_probability, then does not pass it on to other calls. It accepts save_probability via **kwargs and passes it on for Goalie.__init__ to eventually receive. Note that neither Attacker.__init__ nor Goalie.__init__ would have to explicitly include height in their argument lists; it could also be accepted via **kwargs to be eventually received by Player.__init__.
Besides the fact I'm not sure if separate classes is the best way to handle these, the issue is that your constructors can't handle unknown arguments. To allow them to use the *args, **kwargs notation.
Effectively all arguments will be passed to each __init__ and the unused ones ignored.
class Player(object):
def __init__(self, *args, **kwargs):
self.height = kwargs['height']
class Attacker(Player):
def __init__(self, goal_probability, *args, **kwargs):
super().__init__(*args, **kwargs)
self.goal_prob = goal_probability
def hit(self):
pass
# implementation
class Goalie(Player):
def __init__(self, save_probability, *args, **kwargs):
super().__init__(*args, **kwargs)
self.save_prob = save_probability
def catch(self):
pass
# implementation
class UniversalPlayer(Attacker, Goalie):
pass
up = UniversalPlayer(height=1.96, goal_probability=0.6, save_probability=0.2)

Creating class variables in Python with inheritance

I have the following:
class X(object):
def __init__(self, *args, **kwargs):
type(self).label_type = xyzzy(self.__class__.__name__)
class Y(X):
def __init__(self, *args, **kwargs):
super(Y, self).__init__(self, *args, **kwargs)
When I create a new instance of Y, a class variable called label_type is created using Y, not X. This is good and works fine.
But it burns me that I have to wait until there's an instance of Y before the class variable is created. How can I set label_type when class Y is compiled, not when it is instantiated?
EDIT - I have numerous subclasses that are derived from X. I want to push as much of the work into X as possible.
You can use a metaclass to do this kind of thing. Here's a trivial example to demonstrate:
class NameLabelMeta(type):
def __new__(meta, name, bases, dct):
"""Create a new class and label it with its name."""
cls = super(NameLabelMeta, meta).__new__(meta, name, bases, dct)
cls.label_type = name # or xyzzy(name), or whatever
return cls
In use:
>>> class X(object):
__metaclass__ = NameLabelMeta # 2.x syntax
>>> class Y(X):
pass
>>> Y.label_type
'Y'
Note that Y transparently inherits the metaclass from X, so doesn't need to implement anything at all for the correct behaviour to occur. It's also much more efficient, as it doesn't happen again every time you create a new instance of the class.
Do you need to set the label_type dynamically? Why not use a "static variable" such as
class X(object):
LABEL_TYPE = 'X'
class Y(X):
pass
print Y().LABEL_TYPE # Prints 'X'

Pythonic way to have code-reuse in game Entity classes

I'm starting to define my Entity classes for a game I am writing. However, I want a lot of code re-use. I want to define classes for different functionality, and then have classes which 'have' some of these classes' functionality.
For example:
class Collidable:
def handle_collision(other, incident_vector):
pass
def __init__(self, shape):
self.shape = shape
class Movable:
def update_position(self):
self.velocity += self.acceleration
self.position += self.velocity
def __init__(self, velocity, acceleration):
self.velocity, self.acceleration = velocity, acceleration
class Drawable:
def draw(self):
pass
def __init__(self, image):
self.image = image
class Controllable:
def key_down(self, key):
pass
def __init__(self):
pass
Then have a Player class which is Collidable, Movable, Drawable, Controllable, an Invisible Barrier which is only Collidable, a Background which is only Drawable, etc. I've heard of many different ways of connecting multiple classes, (such as via Composition, (Multiple) Inheritance, Interfaces, etc), but I don't know which is most appropriate and/or pythonic for this situation.
Mix-ins (special case of Multiple Inheritance) looks to be what I'm looking for (since a Player should BE a Collidable, a Movable, a Drawable, and a Controllable), but in trying this out, I'm finding difficulty in using super to pass the right arguments to the right init functions.
Edit:
I'm using python 3.2.
Mixins are the way to go, but you don't want to call __init__ on them:
class CollidableMixin(object):
#...
def init_collidable(self, shape):
self.shape = shape
class MovableMixin(object):
#...
def init_movable(self, velocity, acceleration):
self.velocity, self.acceleration = velocity, acceleration
class DrawableMixin(object):
#...
def init_drawable(self, image):
self.image = image
As I see it, you don't need a separate class for Controllable because it just defines an interface which the inheriting class should have. While you do that a lot in statically typed languages like Java, you don't need that in Python. Instead, you just define a key_down method and be done with it. This is called duck typing.
In an example implementation, this will then look like this:
class Player(CollidableMixin, DrawableMixin, MovableMixin):
def __init__(self):
self.init_collidable(...)
self.init_drawable(...)
self.init_movable(...)
def key_down(self, key):
# ...
objects = []
objects.append(Player())
# ... add some more objects. Later we iterate through that collection,
# not knowing which of them is a player:
for o in objects:
try:
o.key_down(...)
except AttributeError:
pass
Here is a simple way to implement the inheritance using super(). For this to work you will always need to create instances of Player (and other classes that inherit from your ***able classes) with keyword arguments. Each base class will strip whatever keyword arguments it is using from kwargs and pass the rest on to the next __init__() in the mro, for example:
class Collidable(object):
def handle_collision(other, incident_vector):
pass
def __init__(self, shape, **kwargs):
self.shape = shape
super(Collidable, self).__init__(**kwargs)
class Movable(object):
def update_position(self):
self.velocity += self.acceleration
self.position += self.velocity
def __init__(self, velocity, acceleration, **kwargs):
self.velocity, self.acceleration = velocity, acceleration
super(Movable, self).__init__(**kwargs)
class Drawable(object):
def draw(self):
pass
def __init__(self, image, **kwargs):
self.image = image
super(Drawable, self).__init__(**kwargs)
class Controllable(object):
def key_down(self, key):
pass
def __init__(self, **kwargs):
super(Controllable, self).__init__(**kwargs)
Then you could define your Player class:
class Player(Collidable, Movable, Drawable, Controllable):
pass
And use it like this:
>>> p = Player(shape='circle', velocity=0.0, acceleration=1.0, image='player.png')
>>> p.shape
'circle'
>>> p.velocity
0.0
>>> p.acceleration
1.0
If you need additional instance variables for the Player class you would define an __init__() similar to the other classes, for example:
class Player(Collidable, Movable, Drawable, Controllable):
def __init__(name, **kwargs):
self.name = name
super(Player, self).__init__(**kwargs)

Categories

Resources