2
class A(SomeBaseClass):
    def __init__(self):
        super().__init__()
        self._attribute_0 = something_0
        self._attribute_1 = something_1
        # etc.

    def some_method(self) -> None:
        do_something()

Now imagine that you have similar classes B, C, D, etc. (not all of them inherit from the same base class). All of them have some functionality that is individual to them, and from a logical point of view well encapsulated in these classes. The key aspect is, that for every of these classes, always only one instance will exist at a time. The problem is, sometimes one class needs access to the other's functions, i.e.

if __name__ == "__main__":
    a = A()
    b = B(a)
    c = C()
    d = D(c, a)
    b.d = d
    etc.

This code is reasonable to me assuming there exist multiple instances of class A, B, etc., but since always only one instance exists at a time, it seems a bit redundant to always pass them around in the constructor (in reality, the classes have of course more complicated names and the "main" function becomes very hard to read...).

One solution, although controversial, in other languages would be the Singelton pattern. And there seem to be ways to implement singeltons in python (see here), but they seem to be discouraged (i.e. some people go as far to say as that they are never needed in python).

My goal is fairly simple: I would like my objects a,b,c, etc. to be accessible in the entire project without having to pass them around in a constructor or set them one-by-one via b.d = d, etc.

Is this possible in Python? What are my options?


Illustration of answer by @blue_note

# objects.py
a = A()
b = B(a)
c = C()
d = D(c, a)
b.d = d

and then when you need them somewhere else:

# foo.py
from objects.py import a

class Foo:
    def __init__(self):
        # use a

2 Answers2

6

Just initialize the objects in a single module, and import them from there. The code in python modules is run only the first time they are imported, and the results are cached. So, on subsequent imports, all modules are guaranteed to access the same instance of the objects.

blue_note
  • 27,712
  • 9
  • 72
  • 90
  • Thanks for the answer! Just to understand correctly, would you use it in the way I edited the question? – mxwQuestion Oct 07 '22 at 14:21
  • @mxwQuestion: yes, it's safe. It's done in python various framework for "global settings" objects (eg, database connections) – blue_note Oct 07 '22 at 14:41
1

I don't know what IDE you are working on, but as I am working with PyCharm I had to find some workarounds for the autocompletition. Without importing classes just to add type hints, I had to name the variables the same as the classes. But this breaks naming conventions

In relation to your question, I would say the same as blue_note. Something along the lines of:

class BaseClass:
    def __init__(self, object_handler):
        self.objecthandler = object_handler


class MyClassA(BaseClass):
    def __init__(self, object_handler):
        super().__init__(object_handler)

    def class_a_method_1(self):
        pass

    def _class_a_private_method_1(self):
        self.objecthandler.myclassb.class_b_method_1()
        self.objecthandler.myclassc.class_c_method_1()


class MyClassB(BaseClass):
    def __init__(self, object_handler):
        super().__init__(object_handler)

    def class_b_method_1(self):
        pass


class MyClassC(BaseClass):
    def __init__(self, object_handler):
        super().__init__(object_handler)

    def class_c_method_1(self):
        pass


class ObjectHandler:
    def __init__(self):
        self.myclassa = MyClassA(self)
        self.myclassb = MyClassB(self)
        self.myclassc = MyClassC(self)

You just add a level of abstraction and can then pass around the ObjectHandler.

Ovski
  • 575
  • 3
  • 14