注册实现一个

时间:2016-01-01 17:15:49

标签: python

以下代码尝试解决所提出的问题,但所提供的模式以不太干净的方式进行。

class Command(object):
    __COMMANDS = {}
    class __metaclass__(type):
        def __init__(cls, name, parents, dct):
            for parent in parents:
                if hasattr(parent, '_Command__COMMANDS'):
                    getattr(parent, '_Command__COMMANDS')[cls.NAME] = cls
            type.__init__(cls, name, parents, dct)

    @classmethod
    def find(cls, command_name):
        """ Returns the Command implementation for a specific command name."""
        return cls.__COMMANDS[command_name]


class Foo(Command):
    NAME = 'foo'

由于派生类也使用父类的相同__metaclass__,因此如果父类具有属性_Command__COMMANDS,则此模式可用于注册所有派生类。

这种模式可能很少引起其他人的争议,例如:

1)它自己的命令也使用元类,但由于它的父类是type类,它没有_Command__COMMANDS属性,它可以正常工作。

2)使用属性测试留下脏代码。不允许使用typeisinstance等函数,但这些函数比使用的模式更清晰。

有人建议改善这一点吗?

1 个答案:

答案 0 :(得分:1)

您可以使用class.__subclasses__() method简单地向班级询问其所有子类的列表:

>>> class Command(object):
...     pass
...
>>> class Foo(Command):
...     NAME = 'foo'
...
>>> Command.__subclasses__()
[<class '__main__.Foo'>]
>>> Command.__subclasses__()[0].NAME
'foo'

您可以使用此方法实现find()类方法:

@classmethod
def find(cls, command_name):
    """Returns the Command implementation for a specific command name."""
    try:
        return next(c for c in cls.__subclasses__() if c.NAME == command_name)
    except StopIteration:
        raise KeyError(command_name)

或者,如果您希望在导入所有命令子类后仅调用find(),则可以将结果缓存在weakref.WeakValueDictionary() object中(以避免循环引用问题):

from weakref import WeakValueDictionary

class Command(object):
    @classmethod
    def find(cls, command_name):
        """Returns the Command implementation for a specific command name."""
        try:
            mapping = cls.__COMMANDS
        except AttributeError:
            mapping = cls.__COMMANDS = WeakValueDictionary({
                c.NAME: c for c in cls.__subclasses__()})
        return mapping[command_name]

您可以通过显式删除Command._Command__COMMANDS类属性来重新清除缓存。

相关问题