我正在尝试编写用于跟踪子类的通用元类
由于我希望这是通用的,我不想在这个元类中硬编码任何类名,因此我想出了一个生成适当元类的函数,如:
def make_subtracker(root):
class SubclassTracker(type):
def __init__(cls, name, bases, dct):
print('registering %s' % (name,))
root._registry.append(cls)
super(SubclassTracker, cls).__init__(name, bases, dct)
return SubclassTracker
这样我就可以调用它来为特定的 root 类生成一个元类:
__metaclass__ = make_subtracker(Root)
这是我遇到问题的地方。我不能这样做:
class Root(object):
_registry = []
__metaclass__ = make_subtracker(Root)
...因为我使用Root
时尚未定义make_subtracker(Root)
。我稍后尝试添加元类属性,以便至少可以在子类中应用它:
class Root(object):
_registry = []
Root.__metaclass__ = make_subtracker(Root)
......但这不起作用。读取类定义时,元类有一个特殊处理,如http://docs.python.org/reference/datamodel.html#customizing-class-creation
中所定义我正在寻找建议来做到这一点(要么在运行时以一种应用于其子类的方式更改一个类'元类,或者任何其他替代方法)。
答案 0 :(得分:9)
Python会自动为新式类执行此操作,如此answer中所述,类似于此处的How can I find all subclasses of a given class in Python?。
答案 1 :(得分:8)
我想你想要这样的东西(未经测试):
class SubclassTracker(type):
def __init__(cls, name, bases, dct):
if not hasattr(cls, '_registry'):
cls._registry = []
print('registering %s' % (name,))
cls._registry.append(cls)
super(SubclassTracker, cls).__init__(name, bases, dct)
然后,对于Python 2,您可以调用它:
class Root(object):
__metaclass__ = SubclassTracker
for Python 3
class Root(object, metaclass=SubclassTracker):
请注意,您不需要在那里粘贴_registry
属性,因为类似的东西就是元类的用途。因为你已经碰巧有一个......;)
另请注意,您可能希望将注册代码移动到else
子句中,以便该类不会将自身注册为子类。
答案 2 :(得分:1)
这是我一直在玩的东西(有效):
def sublass_registry():
''' Create a metaclass to register subclasses '''
class SublassRegistryMeta(type):
def __init__(cls, name, bases, classdict):
if classdict.get('__metaclass__') is SublassRegistryMeta:
SublassRegistryMeta.lineage = [cls] # put root class at head of a list
else:
# sublclasses won't have __metaclass__ explicitly set to this class
# we know they're subclassees because this ctor is being called for them
SublassRegistryMeta.lineage.append(cls) # add subclass to list
type.__init__(cls, name, bases, classdict)
return SublassRegistryMeta
def subclasses(cls):
''' Return a list containing base and subclasses '''
try:
if cls.__metaclass__.lineage[0] is cls: # only valid for a root class
return cls.__metaclass__.lineage
except AttributeError:
pass
return None
class Car(object): # root class
__metaclass__ = sublass_registry()
class Audi(Car): # inherits __metaclass__
pass
class Ford(Car): # inherits __metaclass__
pass
class Audi2(Audi): # sub-subclass also inherits __metaclass__
pass
print subclasses(Car)
# [<class '__main__.Car'>, <class '__main__.Audi'>, <class '__main__.Ford'>, <class '__main__.Audi2'>]
print subclasses(Audi)
# None