I have a number of similar fields in one of my classes modelling json data. All fields are initialized to None to help static tools know they exist then helper functions help initialize them based on a piece of json data that they are modelling (The SecondHandSongs API if you want to know).
Some pieces of data only retrieves the uri of extra data you have to fetch. So I want to use the old trick of initializing a hidden variable to None and fetching/decoding data on first request. But setattr(self.__class__)
looks ugly.
Is there any nicer way to do (setting property dynamically in 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)
edit: I've just realized I was trying to set a property in a instance method called from init . As could be expected it failed the second time around.
edit 2:
Here's how I fixed it after realizing that I was setting a property per object(impossible if not a singleton class)
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)