Python 跟踪子类的泛型元类?
我正在尝试编写一个泛型元类来跟踪子类 因为我希望它是泛型的,所以我不想在这个元类中硬编码任何类名,因此我提出了一个生成适当元类的函数,类似于:Python 跟踪子类的泛型元类?,python,metaclass,Python,Metaclass,我正在尝试编写一个泛型元类来跟踪子类 因为我希望它是泛型的,所以我不想在这个元类中硬编码任何类名,因此我提出了一个生成适当元类的函数,类似于: def make_subtracker(root): class SubclassTracker(type): def __init__(cls, name, bases, dct): print('registering %s' % (name,)) root._registry.
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
通过这种方式,我可以调用它为特定根类生成一个元类,其中包含:
__metaclass__ = make_subtracker(Root)
这就是我遇到问题的地方。我不能这样做:
class Root(object):
_registry = []
__metaclass__ = make_subtracker(Root)
…因为当我使用make_subtracker(Root)
时,Root
尚未定义。稍后我尝试添加uuu元类uuuu属性,以便至少可以在子类中应用它:
class Root(object):
_registry = []
Root.__metaclass__ = make_subtracker(Root)
…但这不起作用__元类在读取类定义时有一个特殊的处理,如中所定义
我正在寻找这样做的建议(或者在运行时以应用于其子类的方式更改类的元类,或者任何其他替代方法) 我想你想要这样的东西(未经测试): 然后,对于Python 2,您可以像这样调用它:
class Root(object):
__metaclass__ = SubclassTracker
对于Python 3
class Root(object, metaclass=SubclassTracker):
请注意,您不需要将\u registry
属性粘贴在那里,因为元类就是用于这些内容的。因为你已经碰巧有一个躺在附近了…;)
还请注意,您可能希望将注册代码移动到
else
子句中,这样该类就不会将自己注册为子类。下面是我一直在玩的东西(可以使用):
def子类注册表():
''创建一个元类来注册子类''
类子类RegistryMeta(类型):
定义初始化(cls、名称、基、类目录):
如果classdict.get(“元类”)是SubassRegistryMeta:
SublassRegistryMeta.Delegate=[cls]#将根类放在列表的开头
其他:
#子类不会显式地将_元类_设置为此类
#我们知道它们是子类,因为这个构造函数是为它们调用的
SublassRegistryMeta.lineage.append(cls)#将子类添加到列表中
类型。初始化(cls、名称、基、类DICT)
返回子类RegistryMeta
def子类(cls):
''返回包含基类和子类的列表''
尝试:
如果cls.\uuuuu元类\uuuuu.relegate[0]是cls:#仅对根类有效
返回cls.\uuuuu元类\uuuuuuuuuuu.沿袭
除属性错误外:
通过
一无所获
类车(对象):#根类
__元类\子类\注册表()
奥迪级(汽车):#继承uu元类__
通过
福特类(汽车):继承元类__
通过
类Audi2(Audi):#子类也继承了uu元类__
通过
打印子类(Car)
# [, , ]
打印子类(奥迪)
#没有
Python会自动为新样式的类执行此操作,正如本文中提到的类似问题一样。事实上,由于子类跟踪器不包含对根类型的任何硬编码引用,因此不需要函数来创建它,因此,我直接定义了一个子类tracker类,而完全忘记了make_子tracker。很好,谢谢@Carles,如果没有定义\u registry
,您上次发表评论时的版本将与多个类中断。我把它修好了,看看新的。它更干净。事实上,我甚至没有想到要去掉工厂函数。FWIW,正如目前所写的,根目录
将自己添加到它的\u注册表
列表中,尽管从技术上讲它本身不是一个子类。这可能是可取的,也可能是不可取的。此外,如果您从Root
派生一个类,那么它不仅会在其\u注册表
列表中包含它自己,而且还会在其中包含Root
,即使它们都不是子类,这似乎很可能是不需要的。请不要这样做。跟在你后面的人会把它撕下来,因为它太复杂了。请使用一个工厂函数来创建相应子类的对象。虽然这在技术上可以跟踪,但取决于您的使用情况,它可能不是很有效。它返回一个列表,因此如果要查找具有特定属性的类,则必须遍历整个列表。使用一个元类可以让您按照自己的意愿高效地索引子类。@Cerin:也许……如果高效率是一个问题,那么这不能从没有指定如何使用信息的问题中确定。不管怎样,我很想看到你的想法的具体实现,因为所有其他答案目前也都是基于列表的——所以请随意添加一个你自己的答案。@martinaeu,我的实现与aaronasterling的相同,只是注册表是字典而不是列表,关键是您希望为每个类使用的任何哈希表示。我目前正在使用它来替代Django中的register()模式,将一个ModelForm类与一个唯一的slug相关联,以便快速发送URL。@Cerin:aaronasterling的答案有一些问题,对Django的引用对我们中的许多人来说意义不大,所以再次发布一些代码或编辑现有的答案。你过于保守了。我没有批评的意思。我只提到了一个我认为其他人可能想考虑的问题。
class Root(object, metaclass=SubclassTracker):
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