How to make python class support item assignment?

19,256

Solution 1

Disclaimer : I might be wrong.

the notation :

self[something]

is legit in the Graph class because it inherits fro dict. This notation is from the dictionnaries ssyntax not from the class attribute declaration syntax.

Although all namespaces associated with a class are dictionnaries, in your class ChildObject, self isn't a dictionnary. Therefore you can't use that syntax.

Otoh, in your class Graph, self IS a dictionnary, since it is a graph, and all graphs are dictionnaries because they inherit from dict.

Solution 2

They are accomplishing this magic by inheriting from dict. A better way of doing this is to inherit from UserDict or the newer collections.MutableMapping

You could accomplish a similar result by doing the same:

import collections

class ObjectChild(collections.MutableMapping):
    def __init__(self, name):
        self['name'] = name

You can also define two special functions to make your class dictionary-like: __getitem__(self, key) and __setitem__(self, key, value). You can see an example of this at Dive Into Python - Special Class Methods.

Solution 3

In Python 3 and later, just add these simple functions to your class:

class some_class(object):
    def __setitem__(self, key, value):
        setattr(self, key, value)

    def __getitem__(self, key):
        return getattr(self, key)

Solution 4

Is using something like this ok?

def mk_opts_dict(d):
    ''' mk_options_dict(dict) -> an instance of OptionsDict '''
    class OptionsDict(object):
        def __init__(self, d):
            self.__dict__ = d

        def __setitem__(self, key, value):
            self.__dict__[key] = value

        def __getitem__(self, key):
            return self.__dict__[key]

    return OptionsDict(d)

Solution 5

I realize this is an old post, but I was looking for some details around item assignment and stumbled upon the answers here. Ted's post wasn't completely wrong. To avoid inheritance from dict, you can make a class inherit from MutableMapping, and then provide methods for __setitem__ and __getitem__.

Additionally, the class will need to support methods for __delitem__, __iter__, __len__, and (optionally) other inherited mixin methods, like pop. The documentation has more info on the details.

from collections.abc import MutableMapping
class ItemAssign(MutableMapping):
    def __init__(self, a, b):
        self.a = a
        self.b = b
    def __setitem__(self, k, v):
        setattr(self, k, v)
    def __getitem__(self, k):
        getattr(self, k)
    def __len__(self):
        return 2
    def __delitem__(self, k):
        self[k] = None
    def __iter__(self):
        yield self.a
        yield self.b

Example use:

>>> x = ItemAssign("banana","apple")
>>> x["a"] = "orange"
>>> x.a
'orange'
>>> del x["a"]
>>> print(x.a)
None
>>> x.pop("b")
'apple'
>>> print(x.b)
None

Hope this serves to clarify how to properly implement item assignment for others stumbling across this post :)

Share:
19,256

Related videos on Youtube

Jack Stout
Author by

Jack Stout

Damaged Portlander. Trying to make things.

Updated on June 17, 2022

Comments

  • Jack Stout
    Jack Stout about 2 years

    While looking over some code in Think Complexity, I noticed their Graph class assigning values to itself. I've copied a few important lines from that class and written an example class, ObjectChild, that fails at this behavior.

    class Graph(dict):
        def __init__(self, vs=[], es=[]):
            for v in vs:
                self.add_vertex(v)
    
            for e in es:
                self.add_edge(e)
    
        def add_edge(self, e):
            v, w = e
            self[v][w] = e
            self[w][v] = e
    
        def add_vertex(self, v):
            self[v] = {}
    
    class ObjectChild(object):
        def __init__(self, name):
            self['name'] = name
    

    I'm sure the different built in types all have their own way of using this, but I'm not sure whether this is something I should try to build into my classes. Is it possible, and how? Is this something I shouldn't bother with, relying instead on simple composition, e.g. self.l = [1, 2, 3]? Should it be avoided outside built in types?

    I ask because I was told "You should almost never inherit from the builtin python collections"; advice I'm hesitant to restrict myself to.

    To clarify, I know that ObjectChild won't "work", and I could easily make it "work", but I'm curious about the inner workings of these built in types that makes their interface different from a child of object.

    • BrenBarn
      BrenBarn over 11 years
      This is tangential to your question, but using [] as a default argument value is likely to cause surprises. See this question.
  • Jack Stout
    Jack Stout over 11 years
    I'll look into these. Thank you.
  • Blckknght
    Blckknght over 11 years
    I think UserDict is obsolete (it's from the days when it was illegal to inherit from dict). MutableMapping also isn't something that provides item access (it's an Abstract Base Class). You need to write (or inherit) __getitem__ and __setitem__ methods, if you want to implement dictionary- or list-like item assignment and access.
  • Zahir J
    Zahir J about 9 years
    Does not work. Downvoted because it would be such an easy thing to check.