Class factory in Python - python

I'm new to Python and need some advice implementing the scenario below.
I have two classes for managing domains at two different registrars. Both have the same interface, e.g.
class RegistrarA(Object):
def __init__(self, domain):
self.domain = domain
def lookup(self):
...
def register(self, info):
...
and
class RegistrarB(object):
def __init__(self, domain):
self.domain = domain
def lookup(self):
...
def register(self, info):
...
I would like to create a Domain class that, given a domain name, loads the correct registrar class based on the extension, e.g.
com = Domain('test.com') #load RegistrarA
com.lookup()
biz = Domain('test.biz') #load RegistrarB
biz.lookup()
I know this can be accomplished using a factory function (see below), but is this the best way of doing it or is there a better way using OOP features?
def factory(domain):
if ...:
return RegistrarA(domain)
else:
return RegistrarB(domain)

I think using a function is fine.
The more interesting question is how do you determine which registrar to load? One option is to have an abstract base Registrar class which concrete implementations subclass, then iterate over its __subclasses__() calling an is_registrar_for() class method:
class Registrar(object):
def __init__(self, domain):
self.domain = domain
class RegistrarA(Registrar):
#classmethod
def is_registrar_for(cls, domain):
return domain == 'foo.com'
class RegistrarB(Registrar):
#classmethod
def is_registrar_for(cls, domain):
return domain == 'bar.com'
def Domain(domain):
for cls in Registrar.__subclasses__():
if cls.is_registrar_for(domain):
return cls(domain)
raise ValueError
print Domain('foo.com')
print Domain('bar.com')
This will let you transparently add new Registrars and delegate the decision of which domains each supports, to them.

Assuming you need separate classes for different registrars (though it's not obvious in your example) your solution looks okay, though RegistrarA and RegistrarB probably share functionality and could be derived from an Abstract Base Class.
As an alternative to your factory function, you could specify a dict, mapping to your registrar classes:
Registrar = {'test.com': RegistrarA, 'test.biz': RegistrarB}
Then:
registrar = Registrar['test.com'](domain)
One quibble: You're not really doing a Class Factory here as you're returning instances rather than classes.

In Python you can change the actual class directly:
class Domain(object):
def __init__(self, domain):
self.domain = domain
if ...:
self.__class__ = RegistrarA
else:
self.__class__ = RegistrarB
And then following will work.
com = Domain('test.com') #load RegistrarA
com.lookup()
I'm using this approach successfully.

You can create a 'wrapper' class and overload its __new__() method to return instances of the specialized sub-classes, e.g.:
class Registrar(object):
def __new__(self, domain):
if ...:
return RegistrarA(domain)
elif ...:
return RegistrarB(domain)
else:
raise Exception()
Additionally, in order to deal with non-mutually exclusive conditions, an issue that was raised in other answers, the first question to ask yourself is whether you want the wrapper class, which plays the role of a dispatcher, to govern the conditions, or it will delegate it to the specialized classes. I can suggest a shared mechanism, where the specialized classes define their own conditions, but the wrapper does the validation, like this (provided that each specialized class exposes a class method that verifies whether it is a registrar for a particular domain, is_registrar_for(...) as suggested in other answers):
class Registrar(object):
registrars = [RegistrarA, RegistrarB]
def __new__(self, domain):
matched_registrars = [r for r in self.registrars if r.is_registrar_for(domain)]
if len(matched_registrars) > 1:
raise Exception('More than one registrar matched!')
elif len(matched_registrars) < 1:
raise Exception('No registrar was matched!')
else:
return matched_registrars[0](domain)

I have this problem all the time. If you have the classes embedded in your application (and its modules) then you can use a function; but if you load plugins dynamically, you need something more dynamic -- registering the classes with a factory via metaclasses automatically.
Here is a pattern I'm sure I lifted from StackOverflow originally, but I don't still have the path to the original post
_registry = {}
class PluginType(type):
def __init__(cls, name, bases, attrs):
_registry[name] = cls
return super(PluginType, cls).__init__(name, bases, attrs)
class Plugin(object):
__metaclass__ = PluginType # python <3.0 only
def __init__(self, *args):
pass
def load_class(plugin_name, plugin_dir):
plugin_file = plugin_name + ".py"
for root, dirs, files in os.walk(plugin_dir) :
if plugin_file in (s for s in files if s.endswith('.py')) :
fp, pathname, description = imp.find_module(plugin_name, [root])
try:
mod = imp.load_module(plugin_name, fp, pathname, description)
finally:
if fp:
fp.close()
return
def get_class(plugin_name) :
t = None
if plugin_name in _registry:
t = _registry[plugin_name]
return t
def get_instance(plugin_name, *args):
return get_class(plugin_name)(*args)

how about something like
class Domain(object):
registrars = []
#classmethod
def add_registrar( cls, reg ):
registrars.append( reg )
def __init__( self, domain ):
self.domain = domain
for reg in self.__class__.registrars:
if reg.is_registrar_for( domain ):
self.registrar = reg
def lookup( self ):
return self.registrar.lookup()
Domain.add_registrar( RegistrarA )
Domain.add_registrar( RegistrarB )
com = Domain('test.com')
com.lookup()

Since the methods are probably shared, using some base class would make sense.
getattr can be used in the factory function to dynamically call another class.
The logic to figure out the registrartype should not be part these classes, but should be in some helper function.
import sys
class RegistrarBase():
"""Registrar Base Class"""
def __init__(self, domain):
self.name = domain
def register(self, info):
pass
def lookup(self):
pass
def __repr__(self):
return "empty domain"
class RegistrarA(RegistrarBase):
def __repr__(self):
return ".com domain"
class RegistrarB(RegistrarBase):
def __repr__(self):
return ".biz domain"
def create_registrar(domainname, registrartype):
try:
registrar = getattr(sys.modules[__name__], registrartype)
return registrar(domainname)
except:
return RegistrarBase(domainname)
domain = create_registrar(domainname = 'test.com', registrartype='RegistrarA')
print(domain)
print(domain.name)
#.com domain
#test.com

Okay, here is an answer based on the answer of Alec Thomas, modified and extended: taking care of multi-level inheritance and ambiguity. If _resolve should be something more complicated than simple check of uniqueness and is likely to change it may be supplied as an argument and not be a class method.
base class module bbb.py:
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import Sequence, Type
class Base(ABC):
def __init__(self, *args, **kwargs):
...
#classmethod
def isit(cls, _s: str) -> bool:
return False
#classmethod
def from_str(cls, s: str, *args, **kwargs) -> Base:
subs = cls._findit(s)
sc = cls._resolve(s, subs)
return sc(*args, **kwargs)
#classmethod
def _findit(cls, s: str) -> Sequence[Type[Base]]:
subs = [cls] if cls.isit(s) else []
subs += [ssc for sc in cls.__subclasses__() for ssc in sc._findit(s)]
return subs
#classmethod
def _resolve(cls, s: str, subs: Sequence[Type[Base]]) -> Type[Base]:
if len(subs) == 0:
raise Exception(f'Cannot find subclass for {s}')
if len(subs) > 1:
raise Exception(
f'Cannot choose unique subclass for {s}: {subs}')
sc = subs[0]
return sc
class B(Base):
#classmethod
def isit(cls, s: str) -> bool:
res = s == 'b class'
return res
enter code here
derived class module ccc.py:
from bbb import Base
class C(Base):
#classmethod
def isit(cls, s: str) -> bool:
res = s == 'c class'
return res
class CC(Base):
#classmethod
def isit(cls, s: str) -> bool:
res = s == 'cc class'
return res
How to use:
In [4]: from bbb import Base
In [5]: import ccc
In [6]: Base.from_str('b class')
Out[6]: <bbb.B at 0x1adf2665288>
In [7]: Base.from_str('c class')
Out[7]: <ccc.C at 0x1adf266a908>
In [8]: Base.from_str('cc class')
Out[8]: <ccc.CC at 0x1adf2665608>

Here a metaclass implicitly collects Registars Classes in an ENTITIES dict
class DomainMeta(type):
ENTITIES = {}
def __new__(cls, name, bases, attrs):
cls = type.__new__(cls, name, bases, attrs)
try:
entity = attrs['domain']
cls.ENTITIES[entity] = cls
except KeyError:
pass
return cls
class Domain(metaclass=DomainMeta):
#classmethod
def factory(cls, domain):
return DomainMeta.ENTITIES[domain]()
class RegistrarA(Domain):
domain = 'test.com'
def lookup(self):
return 'Custom command for .com TLD'
class RegistrarB(Domain):
domain = 'test.biz'
def lookup(self):
return 'Custom command for .biz TLD'
com = Domain.factory('test.com')
type(com) # <class '__main__.RegistrarA'>
com.lookup() # 'Custom command for .com TLD'
com = Domain.factory('test.biz')
type(com) # <class '__main__.RegistrarB'>
com.lookup() # 'Custom command for .biz TLD'

Related

Check unique value when define concrete class for abstract variable in python

Suppose that I have this architecture for my classes:
# abstracts.py
import abc
class AbstractReader(metaclass=abc.ABCMeta):
#classmethod
def get_reader_name(cl):
return cls._READER_NAME
#classmethod
#property
#abc.abstractmethod
def _READER_NAME(cls):
raise NotImplementedError
# concretes.py
from .abstracts import AbstractReader
class ReaderConcreteNumber1(AbstractReader):
_READER_NAME = "NAME1"
class ReaderConcreteNumber2(AbstractReader):
_READER_NAME = "NAME2"
Also I have a manager classes that find concrete classes by _READER_NAME variable. So I need to define unique _READER_NAME for each of my concrete classes.
how do I check that NAME1 and NAME2 are unique when concrete classes are going to define?
You can create a metaclass with a constructor that uses a set to keep track of the name of each instantiating class and raises an exception if a given name already exists in the set:
class UniqueName(type):
names = set()
def __new__(metacls, cls, bases, classdict):
name = classdict['_READER_NAME']
if name in metacls.names:
raise ValueError(f"Class with name '{name}' already exists.")
metacls.names.add(name)
return super().__new__(metacls, cls, bases, classdict)
And make it the metaclass of your AbstractReader class. since Python does not allow a class to have multiple metaclasses, you would need to make AbstractReader inherit from abc.ABCMeta instead of having it as a metaclass:
class AbstractReader(abc.ABCMeta, metaclass=UniqueName):
... # your original code here
Or if you want to use ABCMeta as metaclass in your AbstractReader, just override ABCMeta class and set child ABC as metaclass in AbstractReader:
class BaseABCMeta(abc.ABCMeta):
"""
Check unique name for _READER_NAME variable
"""
_readers_name = set()
def __new__(mcls, name, bases, namespace, **kwargs):
reader_name = namespace['_READER_NAME']
if reader_name in mcls._readers_name:
raise ValueError(f"Class with name '{reader_name}' already exists. ")
mcls._readers_name.add(reader_name)
return super().__new__(mcls, name, bases, namespace, **kwargs)
class AbstractReader(metaclass=BaseABCMeta):
# Your codes ...
So that:
class ReaderConcreteNumber1(AbstractReader):
_READER_NAME = "NAME1"
class ReaderConcreteNumber2(AbstractReader):
_READER_NAME = "NAME1"
would produce:
ValueError: Class with name 'NAME1' already exists.
Demo: https://replit.com/#blhsing/MerryEveryInternet
This is a very special case, but it can be solved with a singleton pattern.
To ease things for our selfes we first create a singleton annotation
# anotations.py
def singleton(clazz):
"""Singleton annotator ensures the annotated class is a singleton"""
class ClassW(clazz):
"""Creates a new sealed class from the object to create."""
_instance = None
def __new__(cls, *args, **kwargs):
if ClassW._instance is None:
ClassW._instance = super(ClassW, cls).__new__(clazz, *args, **kwargs)
ClassW._instance._sealed = False
return ClassW._instance
def __init__(self, *args, **kwargs):
if self._sealed:
return
super(ClassW, self).__init__(*args, **kwargs)
self._sealed = True
ClassW.__name__ = clazz.__name__
return ClassW
Now we construct a singleton Registry class, to register our classes with and to do the checking.
# registry.py
from .annotations import singleton
#singleton
class ReaderRegistry:
"""
Singleton class to register processing readers
### Usage
To register a block call the register function with an ID and the class object.
ReaderRegistry().register('FooName', FooReader)
The class for the block can then be obtained via
Registry()['FooName']
"""
registry = {}
def register(self, key: str, clazz: Type[Block]) -> None:
"""Register a new reader. Names must be unique within the registry"""
if key in self:
raise f"Reader with key {key} already registered."
self.registry[key] = clazz
def __contains__(self, key: str) -> bool:
return key in self.registry.keys()
def __getitem__(self, key: str) -> Type[Block]:
return self.registry[key]
Now, you can
#concretes.py
from .abstracts import AbstractReader
from .registry import ReaderRegistry
class ReaderConcreteNumber1(AbstractReader):
_READER_NAME = "NAME1"
# Note that this is OUTSIDE and AFTER the class definition,
# e.g. end of the file.
RederRegistry().register(ReaderConcreteNumber1._READER_NAME , ReaderConcreteNumber1)
If a reader with such a name exists in the registry already there will be an exception thrown once the file is imported. Now, you can just lookip the classes to construct by theire naume in the registry, e.g.
if reader _namenot in ReaderRegistry():
raise f"Block [{reader _name}] is not known."
reader = ReaderRegistry()[reader _name]

get all functions marked by decorator of a class

I'm trying to store specific actions that are defined within a class.
To reduce code duplication, I would like to make use of a mixin class that stores all the actions based on a decorator.
The idea is that it should be straightforward for other people to extend the classes with new actions. I especially want to avoid that these actions are explicitly listed in the source code (this should be handled by the decorator).
This is what I came up with. Unfortunately, in all .actions lists, all the actions from all the classes are listed.
However, I would like to have a solution that only the actions of the specific class are listed.
class ActionMixin:
actions = []
#staticmethod
def action(fun):
ActionMixin.actions.append(fun)
return fun
class Human(ActionMixin):
#ActionMixin.action
def talk(self):
pass
class Dog(ActionMixin):
#ActionMixin.action
def wuff(self):
pass
class Cat(ActionMixin):
#ActionMixin.action
def miau(self):
pass
if __name__ == "__main__":
party = [Human(), Dog()]
possible_actions = [action for memer in party for action in member.actions]
# I would like that possible_actions is now only Human.talk() and Dog.wuff()
# instead it is 2 times all actions
print(len(possible_actions)) # == 6
I would just write my own descriptor here. So:
class Registry:
def __init__(self):
self._registered = []
def __call__(self, func):
self._registered.append(func)
return func
def __get__(self, obj, objtype=None):
return self._registered
class Human:
actions = Registry()
#actions
def talk(self):
pass
class Dog:
actions = Registry()
#actions
def wuff(self):
pass
class Cat:
actions = Registry()
#actions
def miau(self):
pass
So, instead of inheriting from a mixin, just initialize the descriptor object. Then that object itself can be used as the decorator (the __call__ method!).
Note, the decorator would be whatever name you assigned it, and it would be the name of the attribute where the actions are stored.
In the REPL:
In [11]: party = [Human(), Dog()]
In [12]: [action for member in party for action in member.actions]
Out[12]: [<function __main__.Human.talk(self)>, <function __main__.Dog.wuff(self)>]
EDIT:
You would have to change the implementation if you want this to live in a base class. Basically, use a dict to keep track of the registries, unfortunately, we have to rely on the brittle __qualname__ to get the class in __call__:
class ActionsRegistry:
def __init__(self):
self._registry = {}
def __call__(self, func):
klass_name, func_name = func.__qualname__.rsplit('.', 1)
if klass_name not in self._registry:
self._registry[klass_name] = []
self._registry[klass_name].append(func)
return func
def __get__(self, obj, objtype=None):
if obj is None:
return self
return self._registry[objtype.__qualname__]
class Base:
actions = ActionsRegistry()
class Human(Base):
#Base.actions
def talk(self):
pass
class Dog(Base):
#Base.actions
def wuff(self):
pass
class Cat(Base):
#Base.actions
def miau(self):
pass

Python : use a class methods as static , when its implemented as instance methods

I have a big class which has a lot of functions and attributes.
the instances are created from data in a remote database.
the process of creating each instance is very long and heavy.
In performance sake ive created a bunch class from this heavy class.
so accessing the attributed is easy and works great .
the problem is how to use the methods from that class.
ex :
class clsA():
def __init__(self,obj):
self.attrA=obj.attrA
def someFunc(self):
print self
class bunchClsA(bunch):
def __getattr__(self, attr):
# this is the problem:
try:
#try and return a func
func = clsA.attr
return func
except:
# return simple attribute
return self.attr
Clearly this dosent work , Is there a way i could access the instance function staticly and override the "self" var ?
Found out a nice solution to the problem :
from bunch import Bunch
import types
#Original class:
class A():
y=6
def __init__(self,num):
self.x=num
def funcA(self):
print self.x
#class that wraps A using Bunch(thats what i needed .. u can use another):
class B(Bunch):
def __init__(self, data, cls):
self._cls = cls # notice, not an instance just the class it self
super(B, self).__init__(data)
def __getattr__(self, attr):
# Handles normal Bunch, dict attributes
if attr in self.keys():
return self[attr]
else:
res = getattr(self._cls, attr)
if isinstance(res, types.MethodType):
# returns the class func with self overriden
return types.MethodType(res.im_func, self, type(self))
else:
# returns class attributes like y
return res
data = {'x': 3}
ins_b = B(data, A)
print ins_b.funcA() # returns 3
print ins_b.y # returns 6
And this solves my issue, its a hack and if you have the privileges, redesign the code.

Python shared property parent/child

Embarrassed to ask but I am using webapp2 and I am templating out a solution to make it easier to define routesbased on this google webapp2 route function. But it all depends on being able to define TYPE_NAME at the child level. The idea is the parent sets everything up and the child just needs to implement the _list function. The issue I ran into is TYPE_NAME is None and I need it to be the child.
#main WSGI is extended to have this function
class WSGIApplication(webapp2.WSGIApplication):
def route(self, *args, **kwargs):
def wrapper(func):
self.router.add(webapp2.Route(handler=func, *args, **kwargs))
return func
return wrapper
from main import application
class ParentHandler(RequestHandler):
TYPE_NAME = None
#application.route('/', name="list_%s" %TYPE_NAME)
def list(self):
return self._list()
class ChildHandler(ParentHandler):
TYPE_NAME = 'child'
def _list(self):
return []
I have tried a couple solutions using "class properties" but they didn't pan out. Open to other ideas, I basically just need the child class to inherit the decorated properties and execute them.
Edit:
For all of those on the edge of their seats wondering how I fix this,I was not able to get everything I needed out of the decorator so I ended up using a meta. I also added a _URLS parameter to allow for adding additional "routes". It maps custom function to the route. Really wanted to use a decorator but couldn't get it to work.
class RequestURLMeta(type):
def __new__(mcs, name, bases, dct):
result = super(RequestURLMeta, mcs).__new__(mcs, name, bases, dct)
urls = getattr(result, '_URLS', {}) or {}
for k,v in urls.iteritems():
template = v.pop('template')
app.route(getattr(result, k), template, **v)
if getattr(result, 'TYPE_NAME', None):
app.route(result.list, result.ROOT_PATH, methods=['GET'],name="%s" % result.TYPE_NAME)
#other ones went here..
return result
class ParentHandler(RequestHandler):
__metaclass__ = RequestURLMeta
class ChildHandler(ParentHandler):
TYPE_NAME = 'child'
_URLS = { 'custom': '/custom', 'TYPE_NAME': 'custom_test' }
def _list(self):
return []
def custom(self): pass
I think to get this to work you are going to need to use a metaclass. It might look something like the following (untested):
from main import application
class RouteMeta(type):
def __new__(mcs, name, bases, dct):
type_name = dct.get("TYPE_NAME")
if type_name is not None:
#application.route('/', type_name)
def list(self):
return self._list()
dct["list"] = list
return super(RouteMeta, mcs).__new__(mcs, name, bases, dct)
class ParentHandler(RequestHandler):
__metaclass__ = RouteMeta
class ChildHandler(ParentHandler):
TYPE_NAME = 'child'
def _list(self):
return []
Instead of having the list() method an attribute of ParentHandler, it is dynamically created for classes that inherit from ParentHandler and have TYPE_NAME defined.
If RequestHandler also uses a custom metaclass, have RouteMeta inherit from RequestHandler.__metaclass__ instead of type.
This code:
#application.route('/', name="list_%s" %TYPE_NAME)
def list(self):*emphasized text*
...
is semantically identical to this one:
def list(self):
...
list = application.route('/', name="list_%s" %TYPE_NAME)(list)
i.e. the method route is called inside the ParentHandler scope and
whatever lazy method you try, it will not work. You should try something
different:
from main import application
def route_list(klass):
klass.list = application.route('/',
name="list_%s" % klass.TYPE_NAME)(klass.list)
return klass
class ParentHandler(RequestHandler):
def list(self):
return self._list()
class ChildHandler(ParentHandler):
TYPE_NAME = 'child'
def _list(self):
return []
# in python3 would be:
# #route_list
# class ChildHandler(ParentHandler):
# ...
ChildHandler = route_list(ChildHandler)

Is there a nicer way to do a getter property for dynamically named attribute?

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)
I might subclass property to handle your common cases. Something like this:
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)
If you always want to set the uri as well, you could do something like:
# 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)

Categories