在 Python metaclass 中添加带有 class 的动态 属性
Add dynamic property with class in Python metaclass
我将 mongoengine 与 django rest 框架一起使用。我的模特:
import mongoengine as mongo
class Plan(mongo.Document):
slug = mongo.StringField(max_length=255, primary_key=True)
subplans = mongo.ListField(mongo.EmbeddedDocumentField('self'))
我需要看起来像这样的序列化程序:
class PlanSerializer(serializers.DocumentSerializer):
subplans = PlanSerializer(many=True, required=False)
class Meta:
model = Plan
但是 Python 不正确。所以我使用 metaclass 动态添加 subplans
字段:
class AddSubplanAttrMetaclass(type):
def __new__(cls, name, bases, dct):
# this code is incorrect because PlanSerializer not in globals
class_obj = globals()[name]
dct['subplans'] = class_obj(many=True, required=False)
return super(AddSubplanAttrMetaclass, cls).__new__(cls, name, bases, dct)
class PlanSerializer(serializers.DocumentSerializer, metaclass=AddSubplanAttrMetaclass):
class Meta:
model = Plan
如何在 metaclass 的 __new__
方法中将 PlanSerializer
class 设置为 属性?
你遇到的问题是,当你尝试使用这条线
subplans = PlanSerializer(many=True, required=False)
并且在尝试使用 metaclass 时,行 class_obj = globals()[name]
当您的 PlanSerializerclass 本身尚未定义时。 (在 How is super() in Python 3 implemented? 查看我的回答)
在 metaclass 中做到这一点的正确方法是首先调用超级class 的新对象 - returns 你就是实际的 class 对象,然后调用该对象 - 沿:
class AddSubplanAttrMetaclass(type):
def __new__(metacls, name, bases, dct):
# this code is incorrect because PlanSerializer not in globals
class_obj = super(AddSubplanAttrMetaclass, cls).__new__(metacls, name, bases, dct)
class_obj.subplans = class_obj(many=True, required=False)
return class_obj
但这都不是必需的,并且可能仍然存在问题 - 因为当您仍在 metaclass 的 __new__
中时,并非所有 class 初始化都已完成(甚至 __init__
) 方法。例如,如果 PlanSerializer
的 __init__
方法本身将使用 super
,该调用将失败 - super
只能在 class 之后使用完全初始化。
但是,您根本不需要元class - 您可能只需将 subplans
属性设置为描述符 - 然后懒惰地检索属性。
class PlanSerializer(serializers.DocumentSerializer):
class Meta:
model = Plan
PlanSerializer.subplans = PlanSerializer(many=True, required=False)
我说可能是因为如果 Mongo 需要在初始化 class 本身时设置属性,这将不起作用 - 如果是这种情况,您可以尝试求助于描述符对象.描述符只是一个实现 __get__
方法的对象,如下所示。这通常是使用 @property
装饰器完成的,但这不适用于 class 级别属性,而您在这种情况下需要它。
class PlanSerializer(serializers.DocumentSerializer):
class Subplans(object):
serializer = None
def __get__(self, instance, owner):
if not self.serializer:
self.serializer = PlanSerializer(many=True, required=False)
return self.serializer
subplans = Subplans()
class Meta:
model = Plan
这样,对Subplans
class 的调用延迟到实际使用时,而不是解析class 主体时,它应该可以。
我将 mongoengine 与 django rest 框架一起使用。我的模特:
import mongoengine as mongo
class Plan(mongo.Document):
slug = mongo.StringField(max_length=255, primary_key=True)
subplans = mongo.ListField(mongo.EmbeddedDocumentField('self'))
我需要看起来像这样的序列化程序:
class PlanSerializer(serializers.DocumentSerializer):
subplans = PlanSerializer(many=True, required=False)
class Meta:
model = Plan
但是 Python 不正确。所以我使用 metaclass 动态添加 subplans
字段:
class AddSubplanAttrMetaclass(type):
def __new__(cls, name, bases, dct):
# this code is incorrect because PlanSerializer not in globals
class_obj = globals()[name]
dct['subplans'] = class_obj(many=True, required=False)
return super(AddSubplanAttrMetaclass, cls).__new__(cls, name, bases, dct)
class PlanSerializer(serializers.DocumentSerializer, metaclass=AddSubplanAttrMetaclass):
class Meta:
model = Plan
如何在 metaclass 的 __new__
方法中将 PlanSerializer
class 设置为 属性?
你遇到的问题是,当你尝试使用这条线
subplans = PlanSerializer(many=True, required=False)
并且在尝试使用 metaclass 时,行 class_obj = globals()[name]
当您的 PlanSerializerclass 本身尚未定义时。 (在 How is super() in Python 3 implemented? 查看我的回答)
在 metaclass 中做到这一点的正确方法是首先调用超级class 的新对象 - returns 你就是实际的 class 对象,然后调用该对象 - 沿:
class AddSubplanAttrMetaclass(type):
def __new__(metacls, name, bases, dct):
# this code is incorrect because PlanSerializer not in globals
class_obj = super(AddSubplanAttrMetaclass, cls).__new__(metacls, name, bases, dct)
class_obj.subplans = class_obj(many=True, required=False)
return class_obj
但这都不是必需的,并且可能仍然存在问题 - 因为当您仍在 metaclass 的 __new__
中时,并非所有 class 初始化都已完成(甚至 __init__
) 方法。例如,如果 PlanSerializer
的 __init__
方法本身将使用 super
,该调用将失败 - super
只能在 class 之后使用完全初始化。
但是,您根本不需要元class - 您可能只需将 subplans
属性设置为描述符 - 然后懒惰地检索属性。
class PlanSerializer(serializers.DocumentSerializer):
class Meta:
model = Plan
PlanSerializer.subplans = PlanSerializer(many=True, required=False)
我说可能是因为如果 Mongo 需要在初始化 class 本身时设置属性,这将不起作用 - 如果是这种情况,您可以尝试求助于描述符对象.描述符只是一个实现 __get__
方法的对象,如下所示。这通常是使用 @property
装饰器完成的,但这不适用于 class 级别属性,而您在这种情况下需要它。
class PlanSerializer(serializers.DocumentSerializer):
class Subplans(object):
serializer = None
def __get__(self, instance, owner):
if not self.serializer:
self.serializer = PlanSerializer(many=True, required=False)
return self.serializer
subplans = Subplans()
class Meta:
model = Plan
这样,对Subplans
class 的调用延迟到实际使用时,而不是解析class 主体时,它应该可以。