Question

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.

Était-ce utile?

La solution 3

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.

Autres conseils

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.

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)

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)

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 :)

Your ObjectChild doesn't work because it's not a subclass of dict. Either of these would work:

class ObjectChild(dict):
    def __init__(self, name):
        self['name'] = name

or

class ObjectChild(object):
    def __init__(self, name):
        self.name = name

You don't need to inherit from dict. If you provide setitem and getitem methods, you also get the desired behavior I believe.

class a(object):
    def __setitem__(self, k, v):
        self._data[k] = v
    def __getitem__(self, k):
        return self._data[k]
    _data = {}

Little memo about <dict> inheritance

For those who want to inherit dict. In this case MyDict will have a shallow copy of original dict in it.

class MyDict(dict):
    ...

d = {'a': 1}
md = MyDict(d)

print(d['a'])   # 1
print(md['a'])  # 1

md['a'] = 'new'

print(d['a'])   # 1
print(md['a'])  # new

This could lead to problem when you have a tree of nested dicts and you want to covert part of it to an object. Changing this object will not affect its parent

root = {
    'obj': {
        'a': 1,
        'd': {'x': True}
    }
}
obj = MyDict(root['obj'])

obj['a'] = 2
print(root)   # {'obj': {'a': 1, 'd': {'x': True}}} # 'a' is the same

obj['d']['x'] = False
print(root)   # {'obj': {'a': 1, 'd': {'x': True}}} # 'x' chanded
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top