Frage

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'
War es hilfreich?

Lösung

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()
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top