473,378 Members | 1,688 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,378 software developers and data experts.

Python OOP advice

I'm rewriting a design application for a science fiction game. in it
you design your own starships. Each component has a mass and cost, but
the mass can either be fixed or it can be expressed as a percentage of
the tonnage of the overall ship.

Orriginaly I thought I'd need to have a hull object which contains
component objects, but the component objects need access to members of
the hull object (e.g. the hull size) so that looks messy to implement.

I have defined a base class Component with a class member variable
'hull_size' so that all components can see the hull size. I've then
got two child classes called Fixed_Component and Percent _Component
that implement their mass, mass_percent and cost properties
appropriately for their type. I've also defined a Hull class which
also inherits from Component and provides methods for access to the
hull_size class variable.

I'm not sure where to go from here. One possibility is to have a Ship
object containing a list of components, but I'd need to have a way to
ensure there's only ever one hull object so maybe that shouldn't go in
the list?

I think the fact that the hull_size is a class member also means I
can't ever have an application that loads two designs at the same
time, because they'd share the same hull_size class variable. Is that
so, and is there a way round that? I suspect the inheritance model
will work fine at first, but is too rigid in the long run.

Is there a way to cleanly implement a parent-child relationship
between objects that gives child objects limited access to members of
the parent?

Simon Hibbs
Sep 17 '08 #1
5 940
Simon Hibbs <si*********@gmail.comwrites:
Orriginaly I thought I'd need to have a hull object which contains
component objects, but the component objects need access to members
of the hull object (e.g. the hull size) so that looks messy to
implement.
Was it as messy as this::

class ShipHull(object):
def __init__(self, size):
self.components = dict()
self.size = size

class ShipComponent(object):
def __init__(self, name, hull):
self.name = name
self.hull = hull
I have defined a base class Component with a class member variable
'hull_size' so that all components can see the hull size.
It seems to me the hull is an attribute of the component, and the size
is an attribute of the hull. Why would the hull size be a *class*
attribute?
I've then got two child classes called Fixed_Component and Percent
_Component that implement their mass, mass_percent and cost
properties appropriately for their type.
class FixedShipComponent(ShipComponent):
def _get_mass(self):
return calculation_foo(self.hull.size)
mass = property(_get_mass)

def _get_cost(self):
return calculation_bar(self.hull.size)
cost = property(_get_cost)

class PercentShipComponent(ShipComponent):
def _get_mass(self):
return calculation_spam(self.hull.size)
mass = property(_get_mass)

def _get_cost(self):
return calculation_eggs(self.hull.size)
cost = property(_get_cost)
I've also defined a Hull class which also inherits from Component
and provides methods for access to the hull_size class variable.
I don't see why, if a ShipComponent needs to refer to its hull as
something special, that a ShipHull would subclass ShipComponent.
Is there a way to cleanly implement a parent-child relationship
between objects that gives child objects limited access to members
of the parent?
Sure; have the child instance grow an attribute referencing the
parent, preferably by passing it to the initialisation function
(__init__) of the child.

--
\ Rommel: “Don't move, or I'll turn the key on this can of Spam!” |
`\ —The Goon Show, _Rommel's Treasure_ |
_o__) |
Ben Finney
Sep 17 '08 #2
On Sep 17, 6:50*am, Simon Hibbs <simon.hi...@gmail.comwrote:
I'm rewriting a design application for a science fiction game. in it
you design your own starships. Each component has a mass and cost, but
the mass can either be fixed or it can be expressed as a percentage of
the tonnage of the overall ship.

Orriginaly I thought I'd need to have a hull object which contains
component objects, but the component objects need access to members of
the hull object (e.g. the hull size) so that looks messy to implement.
I would not put this kind of intelligence into the components.

I think the issue here is that your Ship container is not really just
a generic container of ship components, but an assembly with some
specific requirements (must have 1 and only 1 hull, must have 1 or
more engines, etc.) and structure. I would create a class called
ShipDesign that had specific members for those components that have
special logic attached to them, and then more generic list members for
collection-ish components.

Since the hull is such a significant constraint, I would make it an
initialization argument. I would also put some kind of property on
hull representing its "capacity" (oh I see, you have something call
hull_size).

One way to generalize the fixed-cost vs. percentage-cost components
would be to have all components implement a compute_load function that
takes the ShipDesign as an argument. Those that are fixed-cost simply
return their fixed value, those that are percentage-cost can return
their percentage of the ShipDesign's hull.hull_size - this leaves the
door open for other variations on load, that could be based on other
properties besides the hull size.

Here's how I envision your ShipDesign class:

class ShipDesign(object):
def __init__(self, hull):
self.hull = hull
self.engines = []
self.shields = []
self.weapons = []
self.other = []

def compute_consumed_capacity(self):
load = 0
for itemlist in (self.engines, self.shields,
self.weapons, self.other)):
load += sum(item.compute_load(self)
for item in itemlist)
return load

def add_engine(self,e):
engload = e.compute_load(self)
if engload + self.compute_consumed_capacity() >
self.hull.hull_size:
raise ExceededHullCapacityException()
if len(self.engines) == MAXIMUM_ALLOWED_ENGINES:
raise ExceededMaximumConfigurationLimitException()
self.engines.append(e)

def set_hull(self, hull):
if self.compute_consumed_capacity() <= hull.hull_size:
self.hull = hull
else:
raise NewHullTooSmallException()

def is_valid(self):
if not self.engines:
raise InvalidDesignException("must have at least 1
engine")
...etc...

class GenericItem(object):
def __init__(self, name, load):
self.name = name
self.load = load
crewQuarters = GenericItem("Crew Quarters", 50)
disco = GenericItem("Discotheque", 10)
....etc...

Once you have a valid ShipDesign, you can then use it to construct
multiple Ship instances.

Here is how I would work around your "only one hull at a time"
problem. Define several classes for different kinds of hulls:

class CheapHull(Hull):
capacity = 100
name = "EconoHull 1000"
class MediumHull(Hull):
capacity = 500
name = "Mainliner X50"
class TopOTheLineHull(Hull):
capacity = 1000
name = "LuxeMaster 5000"

and then create ship designs with a CheapHull, a MediumHull, or a
TopOTheLineHull. In this case, the member variable of the ShipDesign
is really a class, which you would later use to construct hull
instances as part of making Ship instances from your ShipDesign.

class Ship(object):
def __init__(self, design):
self.hull = design.hull()
self.engines = design.engines[:]
...etc...

This way, each Ship will have its own Hull instance, so that you
can track instance-specific properties, such as damage percentage.

If you don't want to hard-code the hull types, then you can do
something similar with instances of a generic Hull class, which you
would then use as prototypes when constructing Ship instances. Just
be careful that you don't accidentally have all ships sharing the same
hull instance!

-- Paul

Sep 17 '08 #3
Great contributions, thanks both of you. I'm self-tought when it comes
to Python and OOP and I haven't yet grown an intuitive feel for how to
do things sensibly.

Simon
Sep 17 '08 #4
Ben Finney a écrit :
Simon Hibbs <si*********@gmail.comwrites:
>Orriginaly I thought I'd need to have a hull object which contains
component objects, but the component objects need access to members
of the hull object (e.g. the hull size) so that looks messy to
implement.

Was it as messy as this::

class ShipHull(object):
def __init__(self, size):
self.components = dict()
self.size = size

class ShipComponent(object):
def __init__(self, name, hull):
self.name = name
self.hull = hull
>I have defined a base class Component with a class member variable
'hull_size' so that all components can see the hull size.

It seems to me the hull is an attribute of the component, and the size
is an attribute of the hull. Why would the hull size be a *class*
attribute?
>I've then got two child classes called Fixed_Component and Percent
_Component that implement their mass, mass_percent and cost
properties appropriately for their type.

class FixedShipComponent(ShipComponent):
def _get_mass(self):
return calculation_foo(self.hull.size)
mass = property(_get_mass)

def _get_cost(self):
return calculation_bar(self.hull.size)
cost = property(_get_cost)

class PercentShipComponent(ShipComponent):
def _get_mass(self):
return calculation_spam(self.hull.size)
mass = property(_get_mass)

def _get_cost(self):
return calculation_eggs(self.hull.size)
cost = property(_get_cost)
Or use the strategy pattern (dummy example, don't have time to read your
specs !-):

class FixedMassCostStrategy(object):
def get_mass(self, hull):
return calculation_foo(hull.size)
def get_cost(self):
return calculation_bar(hull.size)

class PercentMassCostStrategy(object):
def get_mass(self, hull):
return calculation_spam(hull.size)
def get_cost(self):
return calculation_eggs(hull.size)
class ShipComponent(object):
def __init__(self, name, hull, masscost_strategy):
self.name = name
self._hull = hull # implementation detail
self._strategy = masscost_strategy

mass = property(lambda self: self._strategy.get_mass(self._hull))
cost = property(lambda self: self._strategy.get_cost(self._hull))
Sep 17 '08 #5
Simon Hibbs a crit :
Great contributions, thanks both of you. I'm self-tought when it comes
to Python and OOP and I haven't yet grown an intuitive feel for how to
do things sensibly.
While part of the OO design patterns are mostly workaround for lack of
dynamism in languages like C++ or Java, and while pattern abuse is
certainly not good design, you'd probably learn a lot from the GOF's
Design Patterns book.
Simon
Sep 17 '08 #6

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

20
by: Todd7 | last post by:
I am looking at learning Python, but I would like to know what its strengths and weaknesses are before I invest much time in learning it. My first thought is what is it good for programming,...
114
by: Maurice LING | last post by:
This may be a dumb thing to ask, but besides the penalty for dynamic typing, is there any other real reasons that Python is slower than Java? maurice
1
by: Ron Davis | last post by:
I have recently discovered Python and like it quite a bit. I would like to use it on a new project I am starting. The project will gather data from several web services and present the collected...
9
by: Duncan Smith | last post by:
Hello, I find myself in the, for me, unusual (and at the moment unique) position of having to write a web application. I have quite a lot of existing Python code that will form part of the...
1
by: CloudSolutions | last post by:
Introduction: For many beginners and individual users, requiring a credit card and email registration may pose a barrier when starting to use cloud servers. However, some cloud server providers now...
0
by: Faith0G | last post by:
I am starting a new it consulting business and it's been a while since I setup a new website. Is wordpress still the best web based software for hosting a 5 page website? The webpages will be...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 3 Apr 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome former...
0
by: ryjfgjl | last post by:
In our work, we often need to import Excel data into databases (such as MySQL, SQL Server, Oracle) for data analysis and processing. Usually, we use database tools like Navicat or the Excel import...
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.