以下代码尝试解决所提出的问题,但所提供的模式以不太干净的方式进行。
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)使用属性测试留下脏代码。不允许使用type
或isinstance
等函数,但这些函数比使用的模式更清晰。
有人建议改善这一点吗?
答案 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
类属性来重新清除缓存。