Reputation: 18738
I am looking for a reliable and pythonic way of giving class attributes of a certain type a back-reference to the class they are connected to.
i.e. if there is class definition like the one below, I want to give SomeAttribute()
a reference back to the SomeClass
type object.
class SomeClass(object):
attribute = SomeAttribute()
Currently I have an implementation that relies on a manual method call after creating a class with this kind of attributes. (Example below). The problem with the current approach is that a lot of code needs to check if the reference class has already been populated to the attributes whenever they are accessed.
One solution would be to require that the class is initialized, in which case the code from bind_attributes
could be placed into the __init__
method. However that obviously requires instantiating the class even if an instance is not otherwise needed. I am sure a better solution could be implemented using metaclasses, but I do not know how a pythonic implementation using a metaclass would look.
import inspect
class Attribute(object):
def __init__(self, *params):
self._params = params
self._definition_class = None
def bind(self, definition_class):
self._definition_class = definition_class
def __call__(self):
return self._definition_class.separator.join(self._params)
class Definition(object):
separator = ', '
publications = Attribute('Books', 'Magazines')
clothes = Attribute('Shoes', 'Gloves', 'Hats')
@classmethod
def bind_attributes(cls):
for name, attribute in inspect.getmembers(cls, lambda m: isinstance(m, Attribute)):
attribute.bind(cls)
>>> Definition.bind_attributes()
>>> Definition.publications()
'Books, Magazines'
>>> Definition.clothes()
'Shoes, Gloves, Hats'
Upvotes: 3
Views: 183
Reputation: 23356
You can do this with a metaclass. For example:
class MyMeta(type):
def __new__(mcls, name, bases, members):
cls = type.__new__(mcls, name, bases, members)
for m in members.values():
if isinstance(m, SomeAttribute):
m.bound_cls = cls
return cls
Now of course one downside is this functionality is tied to the class rather than the attribute, so every class you need this functionality with has to use the metaclass:
class SomeClass(object):
__metaclass__ = MyMeta
attribute = SomeAttribute()
Upvotes: 1