How to Link Multiple Classes Together Efficiently - python

Background:
I have been working on a game in Python, and in order to keep everything clean, organized and in a pythonic way, I have a folder containing multiple python files, each containing one big class, for example "MapEngine" or "NPCEngine".
From main.py, I am loading each class from each file and "glueing everything together with a "Game" class, such as:
from folder import *
class Game:
def __init__(self):
self.MapEngine = MapEngine.MapEngine()
...
def loop(self):
...
Since classes such as "CollisionEngine" requires data from other classes such as, "MapEngine", I usually assign some variables in the former (i.e. CollisionEngine) to the latter (i.e MapEngine), in order to use MapEngine's loaded map data or functions:
class CollisionEngine:
def __init__(self, MapClass, ...):
self.MapEngine = MapClass
Problem:
Well, since many classes have to be linked to others, it became hard after a while to figure out which class to load first in order to assign variables. Furthermore, classes like "EventEngine" need to have access to every other class. My code became hard to read, and I have trouble when 2 classes are equally important to each other.
Question:
I have heard of class inheritance, but I do not think it can be applied here because each class is very different as in its function. Therefore, is there a way to beautifully link every class together, as if it was all part of one big class? In other words, is there a way to refer to variables from other classes, from within a class?
(My thoughts: Perhaps, I can write a class called "Request", and it will act as a top level class manager. Although, I think I will have to use functions such as exec() or eval(), which are not efficient and are somewhat dangerous.)
This is my first post, I've tried to be as explicit as possible, please ask me for clarification, & thank you for your reply!

Consider separating your project into layers - that should help you keep things more organised and make the imports more natural.
The principle is that lower layers of your code "cake" shouldn't depend on (read: import) upper layers of your code.
For example you might have a foundation layer which contains common data structures, util classes and algorithms that are used in lots of your code at various layers.
Then you might have a model layer which depends on the foundation layer (i.e. data structures/utils/algorithms) but nothing else. The model layer provides models of objects within the domain.
You might then have a game layer which depends on the model layer (so it would be quite reasonable for modules in your game layer to import things from the model layer, but not vice versa).

Well, after many tries, I have figured out a (sketchy) way of solving my problem. Of course, as eddiewould suggested, I will have a better organization and multiple layers for my code, but if one would like to have multiple classes all linked together, simply include a variable to the main class (that called every class) to every class. I believe that a code snippet will explain it better:
main.py
engine_folder
----> engine_1.py
----> engine_2.py
in main.py, engine_1 and engine_2 are loaded:
from engine_folder import engine_1, engine_2
class game:
def __init__(self):
self.engine_1 = engine_1.engine(self, ...)
self.engine_2 = engine_2.engine(self, ...)
#where engine_1.engine and engine_2.engine are
#two classes that need to transfer data between
#each other
def run(self):
self.engine_1.run()
self.engine_2.run()
Notice how engine_1.engine's first argument is self, which refers to the top level class which called this class. Now, in engine_1, if we would want to print a variable from engine_2, the class would look similar to this:
class engine:
def __init__(self, top_level_class, ...):
self.top_level_class = top_level_class
def run(self):
print self.top_level_class.engine_2.random_var
This is very beautiful (besides the fact that print self.top_level_class.engine_2.random_var is very long), but compared to something like:
class EventEngine:
def __init__(self, Camera_Class, Map_Class, Character_Class, Skill_Class,
Interface_Class, Sprite_Class, Dialog_Class, Game_Class,
Item_Class):
self.ItemEngine = Item_Class
self.GameEngine = Game_Class
self.DialogEngine = Dialog_Class
self.SpriteEngine = Sprite_Class
self.SkillEngine = Skill_Class
self.CameraEngine = Camera_Class
self.MapEngine = Map_Class
self.CharacterEngine = Character_Class
self.IEngine = Interface_Class
The new version:
class EventEngine:
def __init__(self, top_level_class):
self.top = top_level_class
#a var from Map_Class can be called as such:
#self.top.MapEngine.map[0][1]
#and I can call variables from every class, not only those I
#have loaded like before
is much better and much cleaner.

Related

Tricky method for overriding a method in several sibling classes required

Imagine a situation in which a large set of animal classes, which cannot be modified, all inherit from the same parent class "Animal", and each implements a method called "make_noise" each with a slightly different signature, but all with shared parameter volume:
class Cat(Animal)
def make_noise(volume, duration)
-some code here-
class Mouse(Animal)
def make_noise(volume, pitch)
-some different code here-
A different "controller" class, which also cannot be modified, is instructing a list of these animal instances (a list which I have populated) to make sounds at particular volumes (and duration/pitch/etc as appropriate). However, I need to get between the controller and animal classes to modify the behaviour of "make_noise" in all animal classes, so that I can reduce the value of volume before the sound is made.
One option would be to do something like:
def animal_monkeypatcher(animal_class, volume_reduction_factor):
class QuietAnimal(animal_class)
def make_noise(volume, **kwargs)
volume = volume * volume_reduction_factor
super(QuietAnimal, self).make_noise(volume, **kwargs)
However, I also need to pickle these objects, and that doesn't work with this approach. The next approach I thought about was a class which had an instance of the animal like so...
class QuietAnimal():
def __init__(animal_class, init_kwargs):
self.animal = animal_class(**init_kwargs)
def make_noise(volume, **kwargs)
volume = volume * volume_reduction_factor
self.animal.make_noise(volume, **kwargs)
def lots of other functions.....
However, this is also not suitable because the controller sometimes needs to create new instances of animals. It does this by getting the class of an animal (which is QuietAnimal, instead of say Mouse) and then using the same set of init_kwargs to create it, which does not match the signature of QuietAnimal, so again we're stuck...
At the moment I have a horrible hack, which basically forks the init depending on whether or not an animal_class has been passed in or not, and records some info in some class variables. It's frankly dreadful, and not useful if I need to create more than one type of animal (which in my use case I don't, but still...). It's also rubbish because I have to include all of the methods from all of the animals.
What is the appropriate way to wrap/proxy/whatever this set of classes to achieve the above? Some sample code would be greatly appreciated. I am a Python novice.
It turns out, what I needed was standard "monkey patching". Not a great idea either, but slightly cleaner than creating classes on the fly.

How do I use a class method in a separate program?

I have a quite a bit of confusion on how to use classes. I understand what they are, and why they should be used, just not how. For example, we're given a pre-made class (I'll call it class Class_1(object) to keep things simple) with a few functions (methods, right?) and variables in it.
class Class_1(object):
var_1= [a,b,c]
var_2= [x,y,z]
var_3= {n:[o,p],g:[h,i]}
def method_1(self):
'''here's a method'''
(As a side note, the Class_1(object) does have the __init__(self): method already done.)
Now, in a separate program, I've imported the file that contains that class at the top of the program, but how do I use methods or variables from the class? For example, if I want to check a user input against a value in var_1, how would I do that?
I've gotten better with functions in general, but calling on classes and methods is as clear as mud.
Edit: Realized I said "methods" instead of "variables" when I actually need both.
To use the class, you need to create an class instance from the separate file:
import filename1
class1 = filename1.Class_1()
With the instance, you can then access the member variables:
value1 = class1.method_1

Split class definition with full control over subclass

I have a rather lengthy class for data analysis. In this class there are functions for input, output, plotting, different analysis steps and so on. I really would like to split this class to smaller, easier to read subclasses.
The most easy way would of course be to define a superclass and then inherit multiple subclasses. However, this is not what I want because functions of on subclass cannot change the variables of another subclass.
What I want to have is a splitting of the class definition into multiple files where I can group certain methods.
The structure should be something like:
master.py # contains something that puts together all the parts
io.py # contains function for data input / output
plot.py # contains functions for plotting / visualization of data
analyze1.py # contains functions to perform certain analysis steps
analyze2.py # contains functions to perform certain analysis steps
Take a look at mixins:
plot.py:
class DataPlotter(object):
def plot(self):
# lots of code
my_plot_lib.plot(self.data) # assume self.data is available in instance
io.py:
class DataIOProvider(object):
def read(self, filename):
# lots of code
self.data = magic_data
master.py:
from plot import DataPlotter
from io import DataIOProvider
class GodDataProcessor(DataPlotter, DataIOProvider):
def run(self):
self.read('my_file.txt')
self.plot()
Note that you should wrap your code in some package to avoid name clashing (io is a built-in module name in Python).
All base classes may reside in individual modules, and when attribute is set in one of base classes, simply assume it's available in all other classes.

Right way to set variables at python class

Which is the right way to work with variables inside a class?
1- setting them as class attributes where we get them and access them from class itself:
class NeuralNetwork(object):
def __init__(self, topology):
self.topology = topology
self.buildLayers()
def buildLayers(self):
for layer in self.topology:
#do thing
2- passing them through methods that we need them without assign at class if they are not really useful variables:
class NeuralNetwork(object):
def __init__(self, topology):
self.buildLayers(topology)
def buildLayers(self, topology):
for layer in topology:
#do thing
3- a mix of the above two:
class NeuralNetwork(object):
def __init__(self, topology):
self.topology = topology
self.buildLayers(self.topology) # or self.buildLayers(topology) ?
def buildLayers(self, topology):
for layer in topology:
#do thing
I think that the first one is the correct, but it don't let you to reuse the function for different purposes without assigning a new value to the variable, what would look like these:
self.topology = x
self.buildLayers()
What looks weird and you don't really understand that changing self.topology is affecting the call of self.buildLayers()
In general the first way is the "really object oriented" way, and much preferred over the second and the third.
If you want your buildLayers function to be able to change the topology occasionally, give it a param. topology with default value = None.
As long as you don't pass that param. at calling buildLayers, it will use this.topology. If you pass it, it will use the one you passed, and (if you wish) change this.topology to it.
By the way, while it's wise to stick to rules like this in the beginning, there are no real dogmas in programming. As experience grows, you'll find that to each rule there are many perfectly sane exceptions.
That's the fun of programming, you never stop learning from experience.
1st one is correct and recommended. As that will be object dependent.

Class design: methods that share a lot of the same code

I want to create a class with two methods at this point (I also want to be able to
alter the class obviously).
class ogrGeo(object):
def __init__(self):
pass
def CreateLine(self, o_file, xy):
#lots of code
def CreatePoint(self, o_file, xy):
# lot's of the same code as CreateLine(),
# only minor differences
To keep things as clean and to to repeat as
less code as possible I'm asking for some advise. The two methods CreateLine()
and CreatePoint() share a lot of code. To reduce redundance:
Should a define third method that both methods can call?
In this case you could still call
o = ogrGeo()
o.CreateLine(...)
o.CreatePoint(...)seperatly.
Or should I merge them into one method? Is there another solution I haven't thought about or know nothing about?
Thanks already for any suggestions.
Whether you should merge the methods into one is a matter of API design. If the functions have a different purpose, then you keep them seperate. I would merge them if client code is likely to follow the pattern
if some_condition:
o.CreateLine(f, xy)
else:
o.CreatePoint(f, xy)
But otherwise, don't merge. Instead, refactor the common code into a private method, or even a freestanding function if the common code does not touch self. Python has no notion of "private method" built into the language, but names with a leading _ will be recognized as such.
It's perfectly normal to factor out common code into a (private) helper method:
class ogrGeo(object)
def __init__(self):
pass
def CreateLine(self, o_file, xy):
#lots of code
value = self._utility_method(xy)
def CreatePoint(self, o_file, xy):
# lot's of the same code as CreateLine(),
# only minor differences
value = self._utility_method(xy)
def _utility_method(self, xy):
# Common code here
return value
The method could return a value, or it could directly manipulate the attributes on self.
A word of advice: read the Python style guide and stick to it's conventions. Most other python projects do, and it'll make your code easier to comprehend for other Python developers if you do.
For the pieces of code that will overlap, consider whether those can be their own separate functions as well. Then CreateLine would be comprised of several calls to certain functions, with parameter choices that make sense for CreateLine, meanwhile CreatePoint would be several function calls with appropriate parameters for creating a point.
Even if those new auxiliary functions aren't going to be used elsewhere, it's better to modularize them as separate functions than to copy/paste code. But, if it is the case that the auxialiary functions needed to create these structures are pretty specific, then why not break them out into their own classes?
You could make an "Object" class that involves all of the basics for creating objects, and then have "Line" and "Point" classes which derive from "Object". Within those classes, override the necessary functions so that the construction is specific, relying on auxiliary functions in the base "Object" class for the portions of code that overlap.
Then the ogrGeo class will construct instances of these other classes. Even if the ultimate consumer of "Line" or "Shape" doesn't need a full blown class object, you can still use this design, and give ogrGeo the ability to return the sub-pieces of a Line instance or a Point instance that the consumer does wish to use.
It hardly matters. You want the class methods to be as usable as possible for the calling programs, and it's slightly easier and more efficient to have two methods than to have a single method with an additional parameter for the type of object to be created:
def CreateObj(self, obj, o_file, xy) # obj = 0 for Point, 1 for Line, ...
Recommendation: use separate API calls and factor the common code into method(s) that can be called within your class.
You as well could go the other direction. Especially if the following is the case:
def methA/B(...):
lots of common code
small difference
lots of common code
then you could do
def _common(..., callback):
lots of common code
callback()
lots of common code
def methA(...):
def _mypart(): do what A does
_common(..., _mypart)
def methB(...):
def _mypart(): do what B does
_common(..., _mypart)

Categories

Resources