20

I need a python 3.1 deep update function for dictionaries (a function that will recursively update child dictionaries that are inside a parent dictionary).

But I think, in the future, my function could have to deal with objects that behave like dictionaries but aren't. And furthermore I want to avoid using isinstance and type (because they are considered bad, as I can read on almost every Pythonista's blog).

But duck typing is part of Python's philosophy, so how could I check if an object is dictionary-like?

Thanks!

Edit : Thank all for the answers. Just in case, the function I coded can be found at this place : http://blog.cafeaumiel.com/public/python/dict_deep_update.py

David Maust
  • 8,080
  • 3
  • 32
  • 36
thomas
  • 1,855
  • 4
  • 17
  • 19
  • Always take blanket statements on blogs with a grain of salt. I'm a big fan of duck typing and the "easier to ask forgiveness than permission" approach, but see PEP 3119 regarding ABC's as in my answer below for a more nuanced discussion: http://www.python.org/dev/peps/pep-3119/ – Anon Aug 14 '09 at 14:34
  • [Related question](http://stackoverflow.com/questions/8601268/python-class-that-acts-as-mapping-for-unpacking) -- you could test the existence of the mentioned methods there. – jvdm Jan 21 '16 at 17:36

6 Answers6

23

Check out the (new in 3.x) abstract base classes (ABC's) in the collections module:

http://docs.python.org/3.1/library/collections.html

I would consider checking with isinstance against Mapping like in the following:

>>> import collections
>>> isinstance({},collections.Mapping)
True

Then, if you make your own dict-like class, make collections.Mapping one of its bases.

The other route is trying and catching whatever exceptions for the dictionary operations, - but with the recursion you're talking about, I'd rather check against the base type first than handle figuring out what dict or subdict or other dict member was or was not there to throw an exception.

Editing to add: The benefit of checking against the Mapping ABC instead of against dict is that the same test will work for dict-like classes regardless of whether or not they subclass dict, so it's more flexible, just in case.

Anon
  • 11,870
  • 3
  • 23
  • 19
  • 3
    Absolutely right: ABCs are there to let you "program against an interface, not an implementation", just as the Gof4 urges you to!-) – Alex Martelli Aug 14 '09 at 14:29
  • 1
    Gof4 is short for "Gang of Four" which is what the authors of this book are commonly called, since it's easier than saying all their names: http://www.amazon.com/Design-Patterns-Object-Oriented-Addison-Wesley-Professional/dp/0201633612/ref=sr_1_1?ie=UTF8&qid=1250260861&sr=8-1 – Anon Aug 14 '09 at 14:42
  • 1
    Yep, and btw I intensely recommend that book even though my colleague Joe Gregorio disagrees just as intensely (he claims there are no DPs in Python -- heh -- hope we can get a debate on that issue at some future meeting of our local Python user group!-). – Alex Martelli Aug 14 '09 at 14:59
  • 1
    this example should work all the way back to Python 2.4 (when the collections module appeared). It works on Python 2.7 – postfuturist Jul 30 '12 at 22:51
  • 1
    See https://stackoverflow.com/questions/53978542/how-to-use-collections-abc-from-both-python-3-8-and-python-2-7 for both Py 2 and Py 3.8 support – leezu May 27 '19 at 08:43
2

use isinstance, there is nothing wrong with it and it's routinely used in code requiring recursion.

If by dictionary-like you mean the object's class inherit from the dict, isinstance will also return True.

>>> class A(dict):
    pass

>>> a = A()
>>> isinstance(a, dict)
True
SilentGhost
  • 307,395
  • 66
  • 306
  • 293
1

Duck typing is where you do what you want to do, and deal with the fallout if your objects don't behave the way you expected them to.

You want to check if something is dict-like, and update it if it is, right? Just call the object's update method, and handle the Exception you get if there is no such method.

Of course, this will fall flat if you deal with custom class objects which have update methods that do something else entirely -- I'm not quite sure how to deal with that.

sykora
  • 96,888
  • 11
  • 64
  • 71
  • I don't use the update method from the *dict* class. instead I manually walk the dictionary and when a value is a dictionary like object, I try to call recursively my function in order to update it. – thomas Aug 14 '09 at 14:44
1

Well, to update recursively a dictionary, you must call the 'items' method to iterate on it.

So I suggest, you just do :

try :
    for key, value in data.items() :
         # recursive call
except AttributeError :
    # handling the trouble
Bite code
  • 578,959
  • 113
  • 301
  • 329
0

If you also have to handle custom classes that behave like dictionaries but aren't subclassed from dict, you can use getattr to get the function you require.

# loop
# ... more code
someObject = getObject(..)
try:
    getattr(someObject, "update")("someValue")
except AttributeError:
    # Wasn't a type we can handle
Josh Smeaton
  • 47,939
  • 24
  • 129
  • 164
0

I like to check for the '__setitem__' magic method... this that is what allows the foo['bar'] = baz behavior.

if getattr(obj, '__setattr__'):
    obj[key] = val

Here is the python 2.7 docs