Python API设计模式问题
我经常发现自己有一些类的实例,它们像树一样相互关联。比如说,我在用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")