9

I often find I have class instances that are descendants of other class instances, in a tree like fashion. For example say I'm making a CMS platform in Python. I might have a Realm, and under that a Blog, and under that a Post. Each constructor takes it's parent as the first parameter so it knows what it belongs to. It might look like this:

class Realm(object):
    def __init__(self, username, password)

class Blog(object):
    def __init__(self, realm, name)

class Post(object);
    def __init__(self, blog, title, body)

I typically add a create method to the parent class, so the linkage is a bit more automatic. My Realm class might look like this:

class Realm(object):
    def __init__(self, username, password):
        ...
    def createBlog(self, name):
        return Blog(self, name)

That allows the user of the API to not import every single module, just the top level one. It might be like:

realm = Realm("admin", "FDS$#%")
blog = realm.createBlog("Kittens!")
post = blog.createPost("Cute kitten", "Some HTML blah blah")

The problem is those create methods are redundant and I have to pydoc the same parameters in two places.

I wonder if there's a pattern (perhaps using metaclasses) for linking one class instance to a parent class instance. Some way I could call code like this and have the blog know what it's parent realm is:

realm = Realm("admin", "FDS$#%")
blog = realm.Blog("Kittens!")
2
  • You mean the instances are descendants of one another. For a moment, I thought you were reinventing OOP. Commented Mar 5, 2011 at 18:58
  • You're right, I updated the question. Commented Mar 5, 2011 at 19:18

2 Answers 2

1

You could use a common base class for the containers featuring an add() method

class Container(object):
    def __init__(self, parent=None):
        self.children = []
        self.parent = parent
    def add(self, child)
        child.parent = self
        self.children.append(child)
        return child

and make the parent parameter optional in the derived classes

class Blog(Container):
    def __init__(self, name, realm=None):
        Container.__init__(realm)
        self.name = name

Your code above would now read

realm = Realm("admin", "FDS$#%")
blog = realm.add(Blog("Kittens!"))
post = blog.add(Post("Cute kitten", "Some HTML blah blah"))

You wouldn't have any create...() methods any more, so no need to document anything twice.

If setting the parent involves more than just modifying the parent attribute, you could use a property or a setter method.

EDIT: As you pointed out in the comments below, the children should be tied to the parents by the end of the contstructor. The above approach can be modified to support this:

class Container(object):
    def __init__(self, parent=None):
        self.children = []
        self.parent = None
    def add(self, cls, *args)
        child = cls(self, *args)
        self.children.append(child)
        return child

class Realm(Container):
    def __init__(self, username, password):
        ...

class Blog(Container):
    def __init__(self, realm, name):
        ...

class Post(Container):
    def __init__(self, blog, title, body):
        ...

realm = Realm("admin", "FDS$#%")
blog = realm.add(Blog, "Kittens!")
post = blog.add(Post, "Cute kitten", "Some HTML blah blah")
Sign up to request clarification or add additional context in comments.

6 Comments

That works, but I was thinking of some way to tie them together by the end of the constructor running. So that Blog can't exist even temporarily without having a parent (maybe it raises an exception like "this cannot be constructed directly, construct it from the context of a parent" or something).
Your add method needs to return the child here. Currently add doesn't return anything so the line blog = realm.add(..) will result in blog == None
@darkporter: Edited my answer to reflect this.
Hmm, I like that. It gives you bi-directional linkage so you can iterate your children. Let me play around. And answer accepted.
In that case I'd remove the parent from the constructor args. There'd just be a general purpose "parent" property I'd check.
|
0

What about something like this, just subclassing it. In my Realm constructor:

class Realm(object):
    def __init__(self, username, password):
        ...
        parent = self
        original_constructor = blog.Blog.__init__
        class ScopedBlog(blog.Blog):
            def __init__(self, *args):
                self.parent = parent
                original_constructor(self, *args)
        self.Blog = ScopedBlog

Seems to work. And it could be generalized with base classes or meta classes.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.