Python OOP - Class relationships - python

Assuming I have a system of three Classes.
The GameClass creates instances of both other classes upon initialization.
class FieldClass:
def __init__( self ):
return
def AnswerAQuestion( self ):
return 42
class PlayerClass:
def __init__( self ):
return
def DoMagicHere( self ):
# Access "AnswerAQuestion" located in the "FieldClass" instance in "GameClass"
pass
class GameClass:
def __init__( self ):
self.Field = FieldClass()
self.Player = PlayerClass()
What would be the best way of accessing AnswerAQuestion() located in FieldClass from within the instance of PlayerClass?
Do I have to pass a reference to the FieldClass instance to PlayerClass?
Is there another, better way of solving this? Doing the above would make me have to include an additional variable in PlayerClass to hold the FieldClass instance.
Is there a completely different way of managing class relationships in Python?

I would go with Dependency Injection: instantiate a GameClass with the required FieldClass and PlayerClass in the constructor call etc. (i.e. instead of creating the dependent objects from within GameClass as you are doing at the moment).
class GameClass:
def __init__( self, fc, pc ):
self.Field = fc
self.Player = pc
class PlayerClass:
def __init__( self, fc ):
self.fc = fc
def DoMagicHere( self ):
# use self.fc
pass
fc=FieldClass()
pc=PlayerClass(fc)
gc=GameClass(fc, pc)
With DI, you can easily have access to the members you require once the setup phase is completed.

To better understand your class relationships you have to tell us more about your project and what you are trying to accomplish.
One way to reference the instance of FieldClass from PlayerClass in your example is to pass the GameClass instance to the PlayerClass instance:
class PlayerClass:
def __init__(self, game):
self.game = game
def DoMagicHere(self):
self.game.Field.AnswerAQuestion()
# ...
class GameClass:
def __init__( self ):
self.Field = FieldClass()
self.Player = PlayerClass(self)
Another way is to pass the field variable
class PlayerClass:
def __init__(self, field):
self.field = field
def DoMagicHere(self):
self.field.AnswerAQuestion()
# ...
class GameClass:
def __init__( self ):
self.Field = FieldClass()
self.Player = PlayerClass(self.Field)
As a side note: you use a peculiar naming scheme. I suggest this:
class Game:
def __init__(self):
self.field = Field()
self.player = Player(...)
I suggest you read the Python Style Guide before acquiring bad habits.

You could provide the context to the child objects, i.e.:
class FieldClass:
def __init__(self,game):
self.game = game
class PlayerClass:
def __init__(self,game):
self.game = game
def DoMagicHere(self):
self.game.Field.AnswerAQuestion()
class GameClass:
def __init__(self):
self.Field = FieldClass(self)
self.Player = PlayerClass(self)
Since the objects know their context they can reach into it and access other objects in the same context.

I would do something like the following, where you pass Game in as a back-reference, allowing you access to the other associated classes. In this way, you could optionally give that player instance a direct relationship to the field.
class FieldClass(object):
def __init__(self, game):
self.Game = game
def AnswerAQuestion(self):
return 42
class PlayerClass(object):
def __init__(self, game):
self.Game = game
self.Field = game.Field # Optional
def DoMagicHere(self):
self.Game.Field.AnswerAQuestion()
self.Field.AnswerAQuestion() # Optional
class GameClass(object):
def __init__(self):
self.Field = FieldClass(self)
self.Player = PlayerClass(self)
One side note is that it's good practice now to have all your classes inherit from object rather than to have them standing alone.

Related

reference instance attribute in child class

I am trying to call an instance variable from a "parent" class (subclass) to it's "child" class (subsubclass)
class mainclass():
def __init__(self):
self.mainclassvar1 = "mainclass"
class subclass(mainclass):
def __init__(self):
self.subclassvar1 = "subclass"
def changeval(self):
self.subclassvar1 = "subclassedited"
class subsubclass(subclass):
def __init__(self):
self.subsubclassvar1 = subclass.subclassvar1 #<- naturally this fails
def handler():
main=mainclass()
sub = subclass()
sub.changeval()
subsub = subsubclass()
print(subsub.subsubclassvar1)# <- how do I achieve this? I would expect "subclassedited" but it doesn't
if __name__ == "__main__":
handler()
The above does not work obviously but I am trying to show what I am trying to achieve in my head.
if I change the class subsubclass(subclass) as follows it semi-works:
class subsubclass(subclass):
def __init__(self):
subclass.__init__(self)
self.subsubclassvar1 = self.subclassvar1
however the returned value is the original default value of subclass instead of the expected subclassedited.
I am not sure if I should even be trying to do this but I've got some code where the logic has now come to this point and I want to try see if I can get details from the middle class in to the final child class in their final modified states instead of the defaults and without refactoring a lot of code.
Each __init__ method should be invoking the parent's __init__ method, so that the instance is properly initialized for all the classes in the hierarchy.
class mainclass:
def __init__(self):
super().__init__()
self.mainclassvar1 = "mainclass"
class subclass(mainclass):
def __init__(self):
super().__init__()
self.subclassvar1 = "subclass"
def changeval(self):
self.subclassvar1 = "subclassedited"
class subsubclass(subclass):
def __init__(self):
super().__init__()
# Not sure why you would really need this, but...
self.subsubclassvar1 = self.subclassvar1
There's no reason, though that subsub.subclassvar1 should be related to sub.subclassvar1, though. Calling sub.changeval() has nothing to do with subsub.

Access attribute of one instance from another instance defined in that instance

Sorry for the confusing question.
Say there are two instances of two different classes (e.g. 'big_instance' and 'little_instance').
The little_instance is defined as an attribute of the big instance.
How would a method in the little class access an attribute of the big instance.
An example is below.
The line 'return parent.attribute1' is basically pseudo code. How would this line be written properly?
class BigClass:
def __init__(self, att):
self.attribute1 = att
self.little_instance = LittleClass()
class LittleClass:
def parents_att(self):
return parent.attribute1
big_instance = BigClass(1)
print(big_instance.little_instance.parents_att())
ah yes I got it. Read the comments for explanation.
The test code at the end shows that it works even after attribute1 changes :)
class BigClass:
def __init__(self, att):
self.attribute1 = att
# pass in self
self.little_instance = LittleClass(self)
class LittleClass:
def __init__(self, the_big_class):
# the big class is held in an instance var
self.the_big_class = the_big_class
def parents_att(self):
# the instance var is used to reference the attribute
return self.the_big_class.attribute1
big_instance = BigClass(1)
print(big_instance.little_instance.parents_att())
big_instance.attribute1 = 2
print(big_instance.little_instance.parents_att())
You can do the following if you want to access attribute from BigClass into LittleClass.
class BigClass:
def __init__(self, att):
self.attribute1 = att
class LittleClass(BigClass):
def __init__(self, att):
BigClass.__init__(self, att)
def parent_att(self):
return self.attribute1
small_instance = LittleClass(1)
print(small_instance.parent_att)

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.

Combining dict in super class's init and subclass's init automatically?

I'm creating an event system which uses the following class for events:
class Event(set):
def __init__(self, name, iterable=()):
super().__init__(iterable)
self.name = name
def __iadd__(self, listener):
self.add(listener)
return self
def __isub__(self, listener):
self.remove(listener)
return self
def fire(self, **eargs):
for listener in self:
listener(**eargs)
Now I'm trying to create some kind of a dict that would automatically create the events in its __init__ like so:
class EventDict(dict):
def __init__(self, prefix, *event_names):
super().__init__({
name: Event('%s.%s' % (prefix, name))
for name in event_names
})
And here's an example of usage:
class Player:
def __init__(self, name):
self._name = name
self.events = EventDict('Player', 'change_name')
#property
def name(self):
returns self._name
#name.setter
def name(self, value):
old_name = self.name
self.name = value
self.events['change_name'].fire(player=self, old_name=old_name)
Now the problem I'm facing is subclassing.
If I were to subclass my Player class to include also health attribute, I can't use the same way of creating an event dict, cause it would override the existing one and I couldn't access change_name anymore.
So I'm trying to find a way where I can just do something like this (ideal solution):
class Player:
events = EventDict('Player', 'change_name')
class Player2(Player):
events = EventDict('Player2', 'attack', 'kill')
p2 = Player2()
p2.events['change_name'] += my_event_listener # Still access Player class's events
Would something like this be possible?
I know I can do:
class Player2(Player):
def __init__(self, name):
super().__init__()
self.events.update(...)
But it's not the same :P
I think what you want is:
class Player:
EVENTS = ('change_name',)
def __init__(self, name):
self._name = name
self.events = EventDict(
self.__class__.__name__,
*self.EVENTS,
)
...
Then all you need in Player2 is:
class Player2(Player):
EVENTS = Player.EVENTS + ('attack', 'kill')
and the inherited __init__ will work fine.
Stop using EventDict.
The class itself has its own dict which supports inheritance like that.
class Player:
def __init__(self, name):
self._name = name
self.change_name_event = Event('Player.change_name')
class Player2(Player):
def __init__(self, name):
super().__init__(name)
self.attack_event = Event('Player2.attack')
self.kill_event = Event('Player2.kill')
All the events from the subclasses will be added no matter what.
I noticed that maybe you wanted to make it obvious that they're events, so I added 'event' to the names of the fields, but you don't need to if you don't want to.
If you wanted it so that the prefix is the same throughout, then you'd change the strings from something like 'Player.change_name' to self.__class__.__name__ + '.change_name'. That way, it always gets whatever the actual class for the object is. This is part of what #jonrsharpe's solution is trying to get at.
If you wanted to make it so others could add more events dynamically, they can simply do a line like playerObj.my_new_event = Event('Player.my_new_event') or you could provide a nice method in the Player class to make their lives easier:
def add_event(self, event_name):
setattr(self, event_name, Event(self.__class__.__name__ + '.' + event_name)

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