1

I'm trying to model a collection of objects in python (2). The collection should make a certain attribute (an integer, float or any immutable object) of the objects available via a list interface.

(1)

>>> print (collection.attrs)
[1, 5, 3]
>>> collection.attrs = [4, 2, 3]
>>> print (object0.attr == 4)
True

I especially expect this list interface in the collection to allow for reassigning a single object's attribute, e.g.

(2)

>>> collection.attrs[2] = 8
>>> print (object2.attr == 8)
True

I am sure this is a quite frequently occurring situation, unfortunately I was not able to find a satisfying answer on how to implement it on stackoverflow / google etc.

Behind the scenes, I expect the object.attr to be implemented as a mutable object. Somehow I also expect the collection to hold a "list of references" to the object.attr and not the respectively referenced (immutable) values themselves.

I ask for your suggestion how to solve this in an elegant and flexible way.

A possible implementation that allows for (1) but not for (2) is

class Component(object):
    """One of many components."""
    def __init__(self, attr):
        self.attr = attr

class System(object):
    """One System object contains and manages many Component instances.
    System is the main interface to adjusting the components.
    """
    def __init__(self, attr_list):
        self._components = []
        for attr in attr_list:
            new = Component(attr)
            self._components.append(new)

    @property
    def attrs(self):
        # !!! this breaks (2):
        return [component.attr for component in self._components] 
    @attrs.setter
    def attrs(self, new_attrs):
        for component, new_attr in zip(self._components, new_attrs):
            component.attr = new_attr

The !!! line breaks (2) because we create a new list whose entries are references to the values of all Component.attr and not references to the attributes themselves.

Thanks for your input.

TheXMA

2
  • 1
    To do this, you could make System._components a custom class that implements the __setitem__ behaviour you want. Commented Feb 10, 2015 at 8:51
  • Perfect, just as @filmor did below. Commented Feb 10, 2015 at 9:53

2 Answers 2

2

Just add another proxy inbetween:

class _ListProxy:
    def __init__(self, system):
        self._system = system

    def __getitem__(self, index):
        return self._system._components[index].attr

    def __setitem__(self, index, value):
        self._system._components[index].attr = value


class System:
    ...
    @property
    def attrs(self):
        return _ListProxy(self)

You can make the proxy fancier by implementing all the other list methods, but this is enough for your use-case.

Sign up to request clarification or add additional context in comments.

1 Comment

Perfect, this helped a lot! I tried to make it a bit more general in my answer below.
0

@filmor thanks a lot for your answer, this solves the problem perfectly! I made it a bit more general:

class _ListProxy(object):
    """Is a list of object attributes. Accessing _ListProxy entries
    evaluates the object attributes each time it is accessed,
    i.e. this list "proxies" the object attributes.
    """
    def __init__(self, list_of_objects, attr_name):
        """Provide a list of object instances and a name of a commonly
        shared attribute that should be proxied by this _ListProxy
        instance.
        """
        self._list_of_objects = list_of_objects
        self._attr_name = attr_name

    def __getitem__(self, index):
        return getattr(self._list_of_objects[index], self._attr_name)

    def __setitem__(self, index, value):
        setattr(self._list_of_objects[index], self._attr_name, value)

    def __repr__(self):
        return repr(list(self))

    def __len__(self):
        return len(self._list_of_objects)

Are there any important list methods missing?

And what if I want some of the components (objects) to be garbage collected? Do I need to use something like a WeakList to prevent memory leakage?

2 Comments

Answering my latter question: no, popping elements of the list_of_objects also changes the list in System, they are both references to the same list.
I forgot to implement __len__ otherwise I think all important list methods should work on top of these adapted magic methods.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.