让我们有一个名为fields.py
的外部模块,其中包含一个改变类FieldModelMetaclass
的元类FieldModel
:
# module fields.py
class FieldModelMetaclass(type):
def __new__(cls, name, bases, attrs):
# code to alter class creation
class FieldModel(object):
__metaclass__ = FieldModelMetaclass
print 'run at parse time BEFORE metaclass applies'
# module consumer.py
import fields
def adjust_metaclass_new(cls, name, bases, attrs):
# do an extra work on class `cls' and attributes `attrs'
# Is somehow possible from here alter `FieldModelMetaclass.__new__`
# by injection of adjust_metaclass_new to modify FieldModel
# before it gets instantiated ?
mymodel = fields.FieldModel()
据我所知,类的元类在 compile 运行时生效,因此在我导入定义模块的那一刻,类已经受到影响。
如何从外部模块中创建类创建时拦截元类的效果?
答案 0 :(得分:3)
在这种情况下,您无法轻易拦截MetaClass的使用。在FieldModelMetaclass.__new__
主体完成后,导入模块时调用class FieldModel
方法(因此在运行时,而不是编译时)。
理论上,你可以在创建元类之后但在执行class
语句之前使用sys.setrace()
hook注入额外的Python代码,但这种方式就是疯狂。
另一个理论选择是解析模块源代码,重写它(使用ast
module),但同样,这种方式更加疯狂。
如果您想要的是拦截新实例的创建,您只需向元类添加__call__
方法或向元类创建的每个类添加__new__
方法:
import fields
def metaclass__call__(cls, *args, **kw):
instance = super(FieldModelMetaclass, cls).__call__(*args, **kw)
# do something to the instance
return instance
FieldModelMetaclass.__call__ = metaclass__call__
如果您的目标是改变FieldModelMetaclass
生成类对象的方式,那么最好的选择是在导入后再次更改类对象。您始终可以向该类添加更多属性,或使用您自己的元类将其替换为另一个类。