1

I am creating a package with a lot of nested classes. Imagine 2 classes: House and Roof. The House can instantiate a Roof, as well as store it, change its properties, and call its methods. But how about the reverse problem? Can the Roof object find out if it was instantiated by a House and discover anything about that parent object? My hypothetical parent House object is <parent> in this pseudo-code:

class House(object):
    def __init__(self, style:str):
        self.style = style
        self.roof = Roof()

class Roof(object):
    def __init__(self):
        self.type = None
        if <parent>:
            if <parent>.style = 'Rambler':
                self.type = 'Open Gable'
            elif <parent>.style = 'Bungalow':
                self.type = 'Hip'

h = House('Bungalow')

This is just a wild guess at how this might work, but I'm testing to see if a parent class exists, and then want to access its properties. Is this possible?

I know I can pass one parameter (the House's style) to the Roof's __init__, but the real problem I'm trying to solve involves a LOT more properties, which is what I want to avoid.

I have seen packages that solve this by having the Roof class store a property __house, which I presume is to solve this problem. I assume the House passes self to the Roof constructor, but that seems like more coding and I also wonder if it duplicates the objects stored by the program.

thanks!

dcafdg
  • 163
  • 2
  • 6
  • 1
    *"I assume the House passes self to the Roof constructor"* - only if it is passed explicitly. The `Roof` object has no other way of acquiring a reference to the `House` object without being given that reference explicitly. – kaya3 Dec 16 '19 at 23:47
  • Python has great introspection (where you can use the language to find out about the current state of your program and metainformation about the variables) and this answer should give you what you need - this will probably get closed as a dupe: https://stackoverflow.com/questions/2611892/how-to-get-the-parents-of-a-python-class – mgrollins Dec 16 '19 at 23:47
  • Maybe using metaclasses or something, but without passing the `Roof` explicitly around, you're looking for the wrong solution to your problem, I think. – TankorSmash Dec 16 '19 at 23:48
  • @mgrollins this isn't about inheritance, this is basically about finding out what happened a level up the stack. Who the caller of a function was, basically. – TankorSmash Dec 16 '19 at 23:49
  • Oh right. Well, maybe setting `Roof` and others up as an [inner class](https://www.datacamp.com/community/tutorials/inner-classes-python) would build in that backlink? – mgrollins Dec 17 '19 at 00:02
  • No, you have to maintain these relationships yourself. Note on terminology, House is **not a parent** of Roof. But yes, generally, if you want this, you would do something like `self.roof = Roof(self)` where `Roof.__init__` requires a `house` parameter. No, this does not duplicate the objects, there are exactly two objects in that case, the `House` and the `Roof` object, they just each reference each other (which may lead to a memory leak, but the garbage collector should mostly handle that) – juanpa.arrivillaga Dec 17 '19 at 00:10
  • I think that [this discussion](https://stackoverflow.com/questions/7272326/introspect-calling-object) might answer your question. – Gerd Dec 17 '19 at 08:57

1 Answers1

0

There's some technical hacky ways the generated class could figure out who called its constructor, but that would be quite messy.

In your class design, you could make it so that Roof gets passed a reference to House in its constuctor

class Roof:
    def __init__(self, house):
        self.house = house

However, looking at your code, I see a "code smell". You're very strongly tying the two classes together when the way a Roof's style is set depends on the type of the house.

The "cleaner" way would be to allow a Roof to be created with whatever style the creator of the roof desires:

class Roof:
    def __init__(self, style):
        self.style = style

And then inside of House you'd pick which style of Roof goes with which style of house. That way the Roof wouldn't need a reference back to the house anyway.

cadolphs
  • 9,014
  • 1
  • 24
  • 41
  • Thanks for your reply. The objects in my package are pretty tightly integrated, so I'm not too worried about creating dependencies. Do you have any links to the "hacky" solutions? Regarding your ideas, there are potentially a lot of properties to pass around, so the first code block would be much less complicated. – dcafdg Dec 17 '19 at 00:23
  • Well I'd definitely advocate for being explicit about dependencies. So if you think your Roof class really needs a reference back to the House class that spawned it, then passing it in via the constructor (first code block) is the way to go. The "hacky" solution would be something along the lines of https://stackoverflow.com/questions/900392/getting-the-caller-function-name-inside-another-function-in-python – cadolphs Dec 17 '19 at 17:12
  • But I also encourage you to study a bit about what's considered "good" object oriented design. Things like the Single Responsibility Principle, concepts like Coupling versus Cohesion, the Law of Demeter. – cadolphs Dec 17 '19 at 17:13
  • Thanks, I think passing a reference to the parent object in the child's constructor makes sense. I did some testing, and it only stores a reference to it, so no duplication. The house/roof example is just a simplification of the problem. I'm actually creating some classes for an API I use, and the parent object represents a hosted service and the children are things stored there. For some reason, the parent takes a long time to authenticate and open the connection (3rd party, so I have no control). Once I have a connection for the parent, I don't want to keep re-creating it for each child. – dcafdg Dec 31 '19 at 00:52