如何在评估Python类时运行代码

时间:2014-06-15 11:33:06

标签: python

我有一个我要注册的类层次结构,以便以后可以访问它。到目前为止,我一直在手动执行此操作:

class FirstClass(MyBaseClass):
    ....
registry.register(FirstClass)

class SecondClass(FirstClass):
    ....
registry.register(SecondClass)

我正在寻找一种在评估类时调用registry.register(Class)的方法(也就是说,有人用类导入包),而不必明确地调用它。

我想我必须在MyBaseClass添加一些内容,但我无法弄清楚是什么。所有特殊方法似乎都与实例相关,而不是与类相关。

有办法吗?

说明: Registry跟踪从BaseClass派生的类。在代码中的某个时刻,我将遍历所有这些类并实例化对象。它有点复杂,因为注册表也在做其他事情,但这是主要的想法。

3 个答案:

答案 0 :(得分:2)

这是完全元类的用途:将操作应用于整个类的层次结构:

In [1]: def register(cls):
   ...:     # just to show when it is called.
   ...:     print('Register: {}'.format(cls))
   ...: 
   ...: 
   ...: class MyMeta(type):
   ...:     def __new__(cls, name, bases, attrs):
   ...:         the_cls = super().__new__(cls, name, bases, attrs)
   ...:         register(the_cls)
   ...:         return the_cls

In [2]: class FirstClass(metaclass=MyMeta):
   ...:     pass
   ...: 
Register: <class '__main__.FirstClass'>
In [3]: class SecondClass(FirstClass):
   ...:     pass
   ...: 
Register: <class '__main__.SecondClass'>

请注意,您必须仅在层次结构的根中添加元类,并且它将在所有子类中继承,因此您只需将其添加到MyBaseClass即可。

如需深入解释,请参阅此问题:What is a metaclass in Python?


我使用了python3语法。在python2中添加元类,您必须设置__metaclass__属性:

class MyClass(object):
    __metaclass__ = MyMeta

我想指出,要获取类的子类,可以使用__subclasses__方法。没有必要明确地跟踪它们,不要使用自定义元类:

In [4]: FirstClass.__subclasses__()
Out[4]: [__main__.SecondClass]

答案 1 :(得分:2)

Python会自动跟踪任何新式类的直接子类(具有object作为其基类的类或由此派生的基类)在名为__subclasses__的类属性中定义。这意味着您不必自己手动注册它们。

要获得整个继承层次结构,您需要递归地收集每个子类的属性内容。这就是我的意思:

class MyBaseClass(object): pass
class FirstClass(MyBaseClass): pass
class SecondClass(FirstClass): pass

def get_subclasses(cls):
    return cls.__subclasses__() + [g for s in cls.__subclasses__()
                                    for g in get_subclasses(s)]

print(get_subclasses(MyBaseClass))

输出:

[<class '__main__.FirstClass'>, <class '__main__.SecondClass'>]

答案 2 :(得分:0)

我最近的项目需要跟踪类,我最初的方法是使用globals()或类似的东西来查找类,但irc.freenode.org #python中的pythonistas建议使用像这样的装饰器和它看起来真的是pythonic:

registry = {}
def registered(cls):
    registry[cls.__name__] = cls
    return cls

@registered
class Foo:
    """
    """
@registered
class Bar:
    """
    """

导入此文件时,file.registry将包含其中的所有类,并使用注册表['Class_Name']进行查找。在我们的用例中,我们选择了基于Class_Name配置的类实现。