1

Currently I'm making a game server, I actually want to make a base project for all my server products in Python. I used to use C# but I wanted to do something different so I started on Python. Although I don't know how to do something.

In C# I used to make one static 'core' class holding all data, containing the entry point and there I boot the server. Here's a small example:

using System;

namespace SERVER 
{
    public static class Engine
    {
        public static DatabaseHelper DatabaseHelper { get; private set; }

        static void Main(string[] args)
        {
            DatabaseHelper = new DatabaseHelper();
        }
    }
}

Then I just could use in every class:

Engine.DatabaseHelper.SomeMethod();

Now, I want the same concept in my Python project, but I don't know how to do it.

I got this first:

DynamicEmu.py

import Engine

engine = Engine.Engine()

Engine.py

from Network.Game.GameConnectionListener import GameConnectionListener


class Engine:
    gameConnection = None

    def __init__(self):
        gameConnection = GameConnectionListener()

    def tuple_to_addr(self, tpl):
        return '{0}:{1}'.format(tpl[0], str(tpl[1]))

And then in my GameConnectionListener.py I would use:

import DynamicEmu

In order to do

DynamicEmu.engine.tuple_to_addr(TUPLE HERE)

But I get this error

Traceback (most recent call last):
  File "C:/Users/Josh/PycharmProjects/DynamicEmu/DynamicEmu.py", line 1, in <module>
    import Engine
  File "C:\Users\Josh\PycharmProjects\DynamicEmu\Engine.py", line 3, in <module>
    from Network.Game.GameConnectionListener import GameConnectionListener
  File "C:\Users\Josh\PycharmProjects\DynamicEmu\Network\Game\GameConnectionListener.py", line 4, in <module>
    import DynamicEmu
  File "C:\Users\Josh\PycharmProjects\DynamicEmu\DynamicEmu.py", line 3, in <module>
    engine = Engine.Engine()
AttributeError: module 'Engine' has no attribute 'Engine'

I suck in Python so if I'm doing something REALLY wrong I understand, I want to learn so I'd appreciate every piece of help I can get :)

Joshua Bakker
  • 2,288
  • 3
  • 30
  • 63
  • You have a circular dependency: you need an `Engine` object to create a `GameConnectionLIstener`, but you need a `GameConnectionListener` in order to create an `Engine`. – chepner Jul 16 '16 at 12:32
  • Hmm yeah you're right, I understand, any way how I can make something like my C# example then? – Joshua Bakker Jul 16 '16 at 12:34
  • 2
    I don't know what else `Engine` does, but `tuple_to_addr` doesn't use `self`, so I don't know why it is part of the `Engine` class. Make it a standalone function outside the class. (It might work in the same module, but I would move it; circular imports aren't necessarily a problem, but you should try to avoid them anyway.) – chepner Jul 16 '16 at 12:39
  • @chepner The `Engine` class will hold instances to ALL classes that can be used in the whole project: database helper, 'game' helper (the game helper is instance to a `Game` class which contains shop data character data etc instances) etc. AKA. The Engine class has to be accessible from the whole project. The `tuple_to_addr` was just a test but this can be moved to another class I have in mind) – Joshua Bakker Jul 16 '16 at 12:41

1 Answers1

2

Typical Python code doesn't use static members very much.1 So I would advise making most of your variables instance variables. For instance, in DynamicEmu.py, don't have a module-level Engine instance. Instead, have a class which is initialized with an Engine instance

class DynamicEmu:
    def __init__(self, engine):
        self.engine = engine
    ...

or a function that accepts an Engine instance as an argument

def dynamic_emu(engine):
    ...

It's hard to be more specific without knowing what the code in DynamicEmu.py is supposed to do.

Similarly, in GameConnectionListener.py you would define your listener like

class GameConnectionListener:
    def __init__(self, engine):
        self.engine = engine
    ...

and so on with other code. (Bear in mind that Python functions are objects, and can be passed as arguments and used as callbacks. Consider whether you could get away with a function, not a class.)

Obviously, that Engine instance has to be created somewhere. A logical place might be in your main() function, or in some setup function that gets called early in the program. You can then pass it around to other functions and methods as needed.

In my experience, writing a large project tends to work out best when you are clear about the dependencies between different parts of the code. For example, be clear about whether the game engine uses the listener or the listener uses the game engine. If the engine uses the listener, then you should be using GameConnectionListener in Engine, but not vice-versa. The GameConnectionListener should be designed in a way that it doesn't know about the Engine. This isn't always possible, of course, but to whatever extent you can do it, I think it helps. It reduces or eliminates circular dependencies as you have in your example code, and it also helps you write each component of the program separately without having to worry about others at the same time.


1Or, what C# calls static members; in Python they're called class members, but nearly the same functionality is often provided by module-level variables, which are simpler.

David Z
  • 128,184
  • 27
  • 255
  • 279
  • I see, what you do basically is argument passing, why didn't I think of that before? I used to learn at school about argument passing (and I did it in Java too because I had to lol). Thanks for your help! Just one question, how about the imports in both those files, as that might make a difference? – Joshua Bakker Jul 16 '16 at 13:17
  • You may not even need to import `Engine.py` from `DynamicEmu.py` or `GameConnectionListener.py`. In Python, there's no need to import a module just to use an instance of a class that is defined in that module. You only have to import the module if you want to initialize the class, or access one of the module-level variables or functions. I'd advise reading [this other question](https://stackoverflow.com/questions/3188929/why-import-when-you-need-to-use-the-full-name) and my answer to it. (It concerns Java, but I think C# is similar in this respect.) – David Z Jul 16 '16 at 13:21
  • Is it I don't need to import because there's no type definition when I put the `engine` parameter in the `def`? – Joshua Bakker Jul 16 '16 at 13:22
  • Yeah, that's part of it. Python doesn't do type checking (unless you tell it to, explicitly) so there is no need to check whether `engine` actually refers to an instance of the `Engine` class. Similarly, if you access `engine.helper`, Python doesn't need to know whether the type of `engine` actually has a `helper` field or not. Any Python object can have a member of any name; it just checks the object's internal dictionary to see if there is an entry under `helper` and uses that. – David Z Jul 16 '16 at 13:28