我希望能够从列表或字典中动态生成类的属性。这个想法是我可以定义一个属性列表,然后能够使用访问这些属性my_class.my_attribute
例如:
class Campaign(metaclass=MetaCampaign):
_LABELS = ['campaign_type', 'match_type', 'audience_type'] # <-- my list of attributes
for label in _LABELS:
setattr(cls, label, LabelDescriptor(label))
def __init__(self, campaign_protobuf, labels)
self._proto = campaign_protobuf
self._init_labels(labels_dict)
def _init_labels(self, labels_dict):
# magic...
这显然行不通,因为cls
不存在,但我想:
my_campaign = Campaign(campaign, label_dict)
print(my_campaign.campaign_type)
返回 的campaign_type
值campaign
。这显然有点复杂,因为campaign_type
实际上是 a并且做了一些工作来从基础对象Descriptor
中检索一个值。Label
描述符:
class DescriptorProperty(object):
def __init__(self):
self.data = WeakKeyDictionary()
def __set__(self, instance, value):
self.data[instance] = value
class LabelTypeDescriptor(DescriptorProperty):
"""A descriptor that returns the relevant metadata from the label"""
def __init__(self, pattern):
super(MetaTypeLabel, self).__init__()
self.cached_data = WeakKeyDictionary()
# Regex pattern to look in the label:
# r'label_type:ThingToReturn'
self.pattern = f"{pattern}:(.*)"
def __get__(self, instance, owner, refresh=False):
# In order to balance computational speed with memory usage, we cache label values
# when they are first accessed.
if self.cached_data.get(instance, None) is None or refresh:
ctype = re.search(self.pattern, self.data[instance].name) # <-- does a regex search on the label name (e.g. campaign_type:Primary)
if ctype is None:
ctype = False
else:
ctype = ctype.group(1)
self.cached_data[instance] = ctype
return self.cached_data[instance]
这使我可以轻松访问标签的值,如果标签是我关心的类型,它将返回相关值,否则将返回False
.
标签对象:
class Label(Proto):
_FIELDS = ['id', 'name']
_PROTO_NAME = 'label'
# We define what labels can pull metadata directly through a property
campaign_type = LabelTypeDescriptor('campaign_type')
match_type = LabelTypeDescriptor('match_type')
audience_type = LabelTypeDescriptor('audience_type')
def __init__(self, proto, **kwargs):
self._proto = proto
self._set_default_property_values(self) # <-- the 'self' is intentional here, in the campaign object a label would be passed instead.
def _set_default_property_values(self, proto_wrapper):
props = [key for (key, obj) in self.__class__.__dict__.items() if isinstance(obj, DescriptorProperty)]
for prop in props:
setattr(self, prop, proto_wrapper)
因此,如果我的标签(基本上只是一个包装器)中存储了一个 protobuf 标签对象,它看起来像这样:
resource_name: "customers/12345/labels/67890"
id {
value: 67890
}
name {
value: "campaign_type:Primary"
}
然后my_label.campaign_type
会返回Primary
,同样my_label.match_type
会返回False
原因是我正在创建许多以相同方式标记的类,并且可能有很多标签。目前这一切都按描述工作,但我希望能够更动态地定义属性,因为它们基本上都遵循相同类型的模式。所以而不是:
campaign_type = LabelTypeDescriptor('campaign_type')
match_type = LabelTypeDescriptor('match_type')
audience_type = LabelTypeDescriptor('audience_type')
... # (many more labels)
我只是有: _LABELS = ['campaign_type', 'match_type', 'audience_type', ... many more labels]
然后有一些循环来创建属性。
反过来,我可以将类似的方法级联到我的其他类,这样虽然一个Campaign
对象可能包含一个Label
对象,但我可以通过简单地访问标签的值my_campaign.campaign_type
。如果活动没有适当类型的标签,它将简单地返回False
。