0

I have the sense that this must be kind of a dumb question—nub here. So I'm open to an answer of the sort "This is ass-backwards, don't do it, please try this: [proper way]".

I'm using Python 2.7.5.

General Form of the Problem

This causes an infinite loop unless Thesaurus (an app-wide singleton) does not call Baseclass.__init__()

class Baseclass():
    def __init__(self):
        thes = Thesaurus()
        #do stuff

class Thesaurus(Baseclass):
    def __init__(self):
        Baseclass.__init__(self)
        #do stuff

My Specific Case

I have a base class that virtually every other class in my app extends (just some basic conventions for functionality within the app; perhaps should just be an interface). This base class is meant to house a singleton of a Thesaurus class that grants some flexibility with user input by inferring some synonyms (ie. {'yes':'yep', 'ok'}).

But since the subclass calls the superclass's __init__(), which in turn creates another subclass, loops ensue. Not calling the superclass's __init__() works just fine, but I'm concerned that's merely a lucky coincidence, and that my Thesaurus class may eventually be modified to require it's parent __init__().

Advice?

Jonline
  • 1,499
  • 2
  • 20
  • 49
  • 3
    Why would you create an instance of the child in the parent's initializer? – Ignacio Vazquez-Abrams Jun 10 '13 at 18:33
  • Because it's a singleton; only a single instance should ever exist, and everything that extends `Baseclass` should have access to it, and it is needed from the moment the App is initialized. But I'm open to suggestions on how better to implement this! :) – Jonline Jun 10 '13 at 18:35
  • 1
    Why is the call to `Baseclass.__init__` outside the subclass's `__init__`? – BrenBarn Jun 10 '13 at 18:37
  • ... But it infinitely keeps creating itself. – Ignacio Vazquez-Abrams Jun 10 '13 at 18:37
  • @BrenBarn Beats me, like I said—I'm a nub. I hail from PHP where parent init functions are automatically called. This is an artifact of being self-taught. How ought it to be done? – Jonline Jun 10 '13 at 18:39
  • @Jonline: Your code isn't even valid Python because it's missing `def` and the indentation is off. Please post some actual code. – BrenBarn Jun 10 '13 at 18:40
  • 3
    Your code is weird. Are these supposed to be functions or classes? Both are using `def` keyword. – freakish Jun 10 '13 at 18:40
  • @IgnacioVazquez-Abrams For sure, this is indeed my problem. But, because of when in the program each class's functionality is expected to be available, I can't think up another way of implementing this. That Thesaurus object is really needed before the rest of the program gets going because many or most other functions and objects will depend on it's confirming semantic symmetry in various places. : / – Jonline Jun 10 '13 at 18:41
  • Could Baseclass and Thesaurus share a common base? That would fix your issue. – Bi Rico Jun 10 '13 at 18:42
  • @freakish Jesus, sorry, yes; I was just in a hurry when typing out the generic version, thanks. : / – Jonline Jun 10 '13 at 18:42
  • @BiRico Alas, not usefully, no; `Baseclass` contains, among other things, extremely basic functions that even `Thesaurus` needs access to; I just also want those basic functions to have this ability to infer synonyms. – Jonline Jun 10 '13 at 18:44
  • Your problem is design. You are creating subobject in parent class. This **will** lead to issues. Change the design. – freakish Jun 10 '13 at 18:44
  • Do you need a new instance of Thesaurus for every object? – Bi Rico Jun 10 '13 at 18:45
  • @biRico Nope, just one—ever. This is sort of a bootstrapping event; once that instance is created, it serves the entire application. – Jonline Jun 10 '13 at 18:45
  • If you only need one instance of Thesaurus ever, there's no need to store it in `Baseclass`. Just have one global Thesaurus object. – BrenBarn Jun 10 '13 at 18:46
  • oh ..., ez move `thes = Thesaurus()` outside init, even better move it outside the class – Bi Rico Jun 10 '13 at 18:46
  • Haha ok, I'm getting the hint here—there's no syntax that solves this, it's just a poor design. The global object and moving `Thesaurus` outside the `__init__()` are, for reasons idiosyncratic to the program, not ideal, but the point is still well-taken. Thanks, it's always easier to refactor things when you're sure you should be doing it! :P – Jonline Jun 10 '13 at 18:49
  • 1
    Recommended reading/studying: http://stackoverflow.com/questions/6760685/creating-a-singleton-in-python – torek Jun 10 '13 at 19:28
  • @torek Very nice, thanks for that! – Jonline Jun 11 '13 at 15:07

2 Answers2

2

Well, I'm stopping to look at your code, and I'll just base my answer on what you say:

I have a base class that virtually every other class in my app extends (just some basic conventions for functionality within the app; perhaps should just be an interface).

this would be ThesaurusBase in the code below

This base class is meant to house a singleton of a Thesaurus class that grants some flexibility with user input by inferring some synonyms (ie. {'yes':'yep', 'ok'}).

That would be ThesaurusSingleton, that you can call with a better name and make it actually useful.

class ThesaurusBase():
    def __init__(self, singleton=None):
        self.singleton = singleton

    def mymethod1(self):
        raise NotImplementedError

    def mymethod2(self):
        raise NotImplementedError

class ThesaurusSingleton(ThesaurusBase):
    def mymethod1(self):
        return "meaw!"

class Thesaurus(TheraususBase):
    def __init__(self, singleton=None):
        TheraususBase.__init__(self, singleton)

    def mymethod1(self):
        return "quack!"

    def mymethod2(self):
        return "\\_o<"

now you can create your objects as follows:

singleton = ThesaurusSingleton()
thesaurus = Thesaurus(singleton)

edit: Basically, what I've done here is build a "Base" class that is just an interface defining an expected behavior for all its children classes. The class ThesaurusSingleton (I know that's a terrible name) is also implementing that interface, because you said it had too and I did not want to discuss your design, you may always have good reasons for weird constraints.

And finally, do you really need to instantiate your singleton inside the class that is defining the singleton object? Though there may be some hackish way to do so, there's often a better design that avoids the "hackish" part.

What I think is that however you create your singleton, you should better do it explicitly. That's in the "Zen of python": explicit is better than implicit. Why? because then people reading your code (and that might be you in six months) will be able to understand what's happening and what you were thinking when you wrote that code. If you try to make things more implicit (like using sophisticated meta classes and weird self-inheritance) you may wonder what this code does in less than three weeks!

I'm not telling to avoid that kind of options, but to only use sophisticated stuff when you're out of simple ones!

Based on what you said I think the solution I gave can be a starting point. But as you focus on some obscure, yet not very useful hackish stuff instead of talking about your design, I can't be sure if my example is that appropriate, and hint you on the design.

edit2: There's an another way to achieve what you say you want (but be sure that's really the design you want). You may want to use a class method that will act on the class itself (instead of the instances) and thus enable you to store a class-wide instance of itself:

>>> class ThesaurusBase:
...     @classmethod
...     def initClassWide(cls):
...         cls._shared = cls()
... 
>>> class T(ThesaurusBase):
...     def foo(self):
...         print self._shared
... 
>>> ThesaurusBase.initClassWide()
>>> t = T()
>>> t.foo()
<__main__.ThesaurusBase instance at 0x7ff299a7def0>

and you can call the initClassWide method at the module level of where you declare ThesaurusBase, so whenever you import that module, it will have the singleton loaded (the import mechanism ensuring that python modules are run only once).

zmo
  • 22,917
  • 4
  • 48
  • 82
  • Admittedly, I find this tricky to follow so pardon me if I'm misunderstanding. But if I read this correctly, it still doesn't allow me to create a `Thesaurus` instance inside `ThesaurusBase.__init__()`. Ideally, this object would be available to subsequent function calls _inside_ `ThesaurusBase.__init__()`. (Which isn't to be ungrateful, I'm just not sure I understand!) – Jonline Jun 10 '13 at 19:04
  • so no, that object won't be automagically available to others objects based on ThesaurusBase. You'll have to give that singleton as a parameter to all your `ThesaurusBase` based instances. That makes it explicit that all your objects uses the same object that shall not be changed. – zmo Jun 10 '13 at 22:59
  • Truly, this is the spirit of StackExchange, thanks a million guy. The effort you've put into answering this question, not to mention the lucidity and clarity, is laudable. This exactly solves my problem, but moreover I've also just learned how to design for this sort of circumstance going forward. Three cheers man. :D – Jonline Jun 11 '13 at 15:06
  • Though as a follow-up—and not to impose on your further!—since you seemed willing to comment on design... I work in a psychology lab, what I'm building is a small-scale development framework for a select set of scientists who routinely do experiments in python wherein they rewrite the same functionality over and over (because they are even worse software engineers than I). So my goal has been to create, as you say, a 'metaclass' than creates a generic skeleton of an experiment. But rather than aiming for efficiency, extensibility, etc., my biggest aim is actually to create something.... – Jonline Jun 11 '13 at 15:13
  • ... that can be used by an idiot, wherein `true` and `TRUE` and `'true'` are all accepted, or the tuples and lists can be used interchangeably. These are bad ideas in almost every context, but these guys—they _aren't_ going to learn, it's not part of their degrees or curriculi, they are learning exactly as little Python as they can. So I am looking to create as many "automagic" solutions as possible, one of which is a convention of always creating an instance of an `App` class that they can configure without needing to learn OOP or advance their python beyond very basic statements. – Jonline Jun 11 '13 at 15:16
  • So my metaclass, `App`, bootstraps to itself various items depending on the contents of a `config` dict (mutable signatures are bad, I know, but dict's allow for English so these guys remember more) provided at instantiation. To that end, I want the 'training wheels' (ie. `Thesaurus`, an `Inflector`, etc.) to be available immediately. I think, though, that I'm going to go with your singleton solution because I think I can order things so that my poor scientists never have to know about it/instantiate it. If you have any suggestions, though, boy am I open to them, haha. Otherwise, thanks again. – Jonline Jun 11 '13 at 15:23
  • 1
    well, trying to make the language support several syntaxes, more ways to do one thing is, *imho*, a big mistake. If you want a good example of something that got idiot proof, while being one of the hardest language to learn, it's Arduino and C++. For sure, they will not learn the syntax, but if you make your design enable them to work with a no more than a few pages handbook, that everything *they* need to know is explicit (while everything they don't need to know is still explicit, but hidden to their eyes), but that's still does not accept ambiguity in the semantics, I think you'll win. – zmo Jun 12 '13 at 10:06
  • Food for thought... god I love StackExchange... thanks again zmo, contributions appreciated. :) – Jonline Jun 13 '13 at 13:20
0

the short answer is:

do not instantiate an instance of a sub class from the super class constructor

longer answer:

if the motive you have to try to do this is the fact the Thesaurus is a singleton then you'll be better off exposing the singleton using a static method in the class (Thesaurus) and calling this method when you need the singleton

Francisco Meza
  • 845
  • 5
  • 8
  • Thanks, though not as elaborate an answer as the one from zmo, it is indeed the right one (I've realized) and I appreciate the effort. – Jonline Jun 11 '13 at 15:06