Python API设计模式问题

9 投票
2 回答
1630 浏览
提问于 2025-04-16 13:04

我经常发现自己有一些类的实例,它们像树一样相互关联。比如说,我在用Python做一个内容管理系统(CMS)平台。我可能会有一个“领域”(Realm),在这个领域下有一个“博客”(Blog),而在博客下又有一篇“文章”(Post)。每个构造函数的第一个参数都是它的父类,这样它就知道自己属于哪个部分。代码可能像这样:

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)

我通常会在父类中添加一个创建方法,这样连接就会变得更自动化。我的“领域”类可能看起来像这样:

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

这样,使用这个API的人就不需要导入每一个模块,只需要导入最顶层的一个。可能像这样:

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

问题是这些创建方法有点多余,我必须在两个地方写相同的参数说明。

我在想是否有一种模式(也许是使用元类)可以将一个类的实例与其父类的实例连接起来。有没有什么方法可以让我这样调用代码,让博客知道它的父领域是什么:

realm = Realm("admin", "FDS$#%")
blog = realm.Blog("Kittens!")

2 个回答

0

那这样做怎么样呢,直接继承它。在我的Realm构造函数里:

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

看起来可以正常工作。而且可以通过基类或元类来进行更广泛的应用。

1

你可以为这些容器使用一个共同的基类,并在里面添加一个 add() 方法。

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

在派生类中,可以把 parent 参数设为可选。

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

这样,你上面的代码就可以变成这样:

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

你就不需要再写 create...() 这种方法了,所以也不用重复写文档。

如果设置父级的过程不仅仅是修改 parent 属性,你可以使用一个属性或者一个设置方法。

编辑:正如你在下面的评论中提到的,子类应该在构造函数结束时与父类关联。上面的方法可以修改来支持这一点:

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")

撰写回答