我的一个类中有很多类似的字段来建模json数据。所有字段都初始化为None以帮助静态工具知道它们存在然后帮助函数根据它们正在建模的json数据帮助初始化它们(The SecondHandSongs API如果你想知道的话)。
某些数据只检索您必须获取的额外数据。所以我想使用将隐藏变量初始化为None并在第一次请求时获取/解码数据的旧技巧。但setattr(self.__class__)
看起来很难看。
有没有更好的方法(在python中动态设置属性)?
def _initialize_url_fields(self, attrNamesToFactoryFunction, json_data):
for (name, factoryFunction) in attrNamesToFactoryFunction.iteritems():
try:
url = json_data[name]
except KeyError:
continue
setattr(self, name + "_url", url)
setattr(self, "_" + name, None)
setattr(self.__class__, name, property(lambda s: s._getter("_" + name, url, factoryFunction)))
def _getter(self, hidden_prop_name, url, factoryFunction):
if not getattr(self, hidden_prop_name):
json_data = SHSDataAcess.getSHSData(url)
setattr(self, hidden_prop_name, factoryFunction(json_data))
return getattr(self, hidden_prop_name)
编辑: 我刚刚意识到我正在尝试在从 init 调用的实例方法中设置属性 。可以预料,它第二次失败了。
编辑2:
这是我在意识到每个对象设置一个属性后修复它的方法(如果不是单例类,则不可能)
class ShsData(object):
def _initialize_url_fields(self, attrNamesToFactoryFunctions, json_data):
for (name, factoryFunction) in attrNamesToFactoryFunctions.items():
self._getter_factory_functions[name] = factoryFunction
uri = None
try:
uri = json_data[name]
except KeyError:
pass
setattr(self, name + "_uri", uri)
setattr(self, "_" + name, None)
def _fetch_shs_data_on_first_access_getter(base_prop_name):
def getter(self):
factoryFunction = self._getter_factory_functions[base_prop_name]
hidden_prop_name = "_" + base_prop_name
uri_prop_name = base_prop_name + "_uri"
if not getattr(self, hidden_prop_name):
if getattr(self, uri_prop_name):
json_data = SHSDataAcess.getSHSData(getattr(self, uri_prop_name))
setattr(self, hidden_prop_name, factoryFunction(json_data))
else:
return None
return getattr(self, hidden_prop_name)
return getter
class ShsArtist(ShsData):
performances_data = property(_fetch_shs_data_on_first_access_getter("performances"))
creditedWorks_data = property(_fetch_shs_data_on_first_access_getter("creditedWorks"))
releases_data = property(_fetch_shs_data_on_first_access_getter("releases"))
def __init__(self, json_data):
...
self._initialize_url_fields({"performances": lambda xs: [ShsPerformance(x) for x in xs],
"creditedWorks": lambda xs: [ShsWork(x) for x in xs],
"releases": lambda xs: [ShsRelease(x) for x in xs]},
json_data)
答案 0 :(得分:0)
我可能会将属性子类化以处理您的常见情况。像这样:
class shs_klass_property(property):
def __init__(self, name, klass):
self.name = name
self.klass = klass
self.cached_name = '_%s' % name
super(shs_klass_property, self).__init__(self.compute)
def compute(self, obj):
if not hasattr(obj, self.cached_name):
if self.name in obj._json_data:
# if needed handle cases where this isn't a list
val = [self.klass(x) for x in obj._json_data[self.name]]
else:
val = None
setattr(obj, self.cached_name, val)
return getattr(obj, self.cached_name)
class ShsData(object):
def __init__(self, json_data):
self._json_data = json_data
class ShsWork(ShsData):
pass
class ShsArtist(ShsData):
works = shs_klass_property('works', ShsWork)
如果您总是想要设置uri,您可以执行以下操作:
# if you change this to pass in "_json_data" too,
# you'd have a simple general purpose delegation decorator
class shs_json_property(property):
def __init__(self, name):
self.name = name
super(shs_json_property, self).__init__(self.compute)
def compute(self, obj):
return obj._json_data.get(self.name, None)
# a helper to set both. not necessary but saves a line of code.
def shs_property_pair(name, klass):
return (shs_klass_property(name, klass),
shs_json_property(name))
class ShsArtist(ShsData):
works, works_uri = shs_property_pair('works', ShsWork)