Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/305.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 跟踪子类的泛型元类?_Python_Metaclass - Fatal编程技术网

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