3

Are there benefits to using dictionaries instead of objects in Python (or vice versa) when all you're doing is describing something's properties?

The project I'm working on currently has a number of places where dictionaries are used where I would have normally created objects. In my mind objects provide more structure and allow for better programmer-error checking by programs such as pylint, but it's difficult to explain why I would use an object rather than a dict.

For a mock example, one module creates Widgets and contains a method such as this:

def create(self, propertyA, propertyB=55, propertyC="default", 
           propertyD=None, propertyE=None, propertyF=None, propertyG=None,
           propertyH=None, propertyI=None):

That method would be called by creating a dictionary and passing it in much like this:

widget_client = WidgetClient()
widget = {
    "propertyA": "my_widget",
    "propertyB": 10,
    ...
}
widget_client.create(**widget)

When I see this, I see that every single one of those properties is what describes a 'Widget' and want to do the following:

class Widget(object):
    """Represents a widget."""

    def __init__(self, propertyA, **kwargs):
        """Initialize a Widget.

        :param propertyA: The name of the widget.
        :param kwargs: Additional properties may be specified (see below).
        :returns: None

        """
        self.propertyA = propertyA
        self.propertyB = kwargs.get("propertyB", 55)
        self.propertyC = kwargs.get("propertyC", "default")
        self.propertyD = kwargs.get("propertyD", None)
        self.propertyE = kwargs.get("propertyE", None)
        self.propertyF = kwargs.get("propertyF", None)

And then update the create() method to look something like this:

def create(self, widget):

Which ends up being called like this:

widget_client = WidgetClient()
widget = Widget(propertyA="my_widget")
widget.propertyB = 10
...
widget_client.create(widget)

In my mind this is clearly better, but I've been wrong in the past and I can't think of how to explain myself. Of course I'm still using **kwargs which could be avoided by breaking the Widget down into smaller component/related parts and creating more objects etc etc, but I feel this is a good "first step". Does this make any sense at all?

Dictionary Benefits:

  1. Faster and/or more memory efficient

Dictionary Drawbacks:

  1. Inability to catch some errors with static code checkers
  2. A full list of all widget properties may never appear or be known

Objects Benefits:

  1. Knowing exactly what a 'Widget' is comprised of
  2. Potentially catch errors with static code checkers (although the use of ** magic prevents some of that)

Object Drawbacks:

  1. Slower and/or less memory efficient

This seems like a silly question, but why do something with objects that can be done with dictionaries?

  • 6
    Have you considered [namedtuple](http://docs.python.org/library/collections.html#collections.namedtuple)? – jterrace Aug 28 '11 at 22:55
  • partial duplicate of [Do I need to learn about objects, or can I save time and just learn dictionaries?](http://stackoverflow.com/questions/7049637/do-i-need-to-learn-about-objects-or-can-i-save-time-and-just-learn-dictionaries) and others. Just search for questions tagged `[python]` and `[oop]`. – agf Aug 28 '11 at 23:21
  • Maybe you should switch to Javascript, where objects are dictionaries so the whole problem disappears :-) – Tomasz Zieliński Aug 28 '11 at 23:22
  • Also, "objects" isn't really correct here (as my answer to the linked question explains) you really mean user-defined classes. – agf Aug 28 '11 at 23:28
  • 3
    "Faster and/or more memory efficient" and "Slower and/or less memory efficient" is unpythonic talk. If you need to care about these you shoudn't be using Python. So now you are only left with **Dictionary Drawbacks** and **Objects Benefits**. The pythonic choice should be obvious by now – John La Rooy Aug 29 '11 at 00:39
  • @gnibbler: I'm all about objects, but can we work in concrete terms instead of using 'unpythonic'? This word seems to come up a lot and there isn't exactly a universal definition that I've found. – Brian Lamar Aug 31 '11 at 20:48
  • @jterrace: Unfortunately confined at the moment to 2.5+, so I'm leaning heavily towards __slots__ which give me some of the same benefits as namedtuples. Thanks! – Brian Lamar Aug 31 '11 at 20:49
  • I feel your pain: 2.5 compatibility is a nightmare. – jterrace Aug 31 '11 at 21:08
  • @Brian, type `import this` into a Python interpreter. Add PEP8 and you'll have a good set of guidelines. You should be trying to make your code readable and maintainable. – John La Rooy Sep 01 '11 at 00:42
  • @Brian, namedtuple recipe for Python2.4+ http://code.activestate.com/recipes/500261-named-tuples/ – John La Rooy Sep 01 '11 at 00:44

4 Answers4

2

No, there are no benefits to using dictionaries instead of objects - data in an object ARE normally stored in a dictionary.

There might be benefits to using objects instead of dictionaries. See: http://docs.python.org/reference/datamodel.html#slots

rczajka
  • 1,810
  • 14
  • 13
2

Using any built-in data type always gives you the advantage of some functionality, plus its behavior is well known to other programmers. A dictionary gives you a fist-full of built in methods, and nobody has to wonder if it's iterable.

That is just one advantage. Not that I am saying you should always use dictionaries over declaring your own objects. (and of course your new object can inherit dictionary-like behaviors) But you shouldn't necessarily always opt for an creating a new object when a simpler storage mechanism may do. Using comprehension as the guide, it would depend on whether Widget had any special behaviors or attributes.

zenWeasel
  • 1,889
  • 2
  • 22
  • 27
  • 1
    _Dictionaries are objects_, it's important not to gloss over this point. – agf Aug 28 '11 at 23:36
  • This is a good answer. In the OP's example, `Widget` inherits from `object`, not `dict`. If you want `Widget` to have the full functionality of a Python dictionary, you have to reimplement a load of methods, or inherit from `dict` or one of its subclasses. – RoundTower Aug 29 '11 at 01:22
  • What if I don't need the object to be iterable or the other 'fist-full' of built-in dictionary methods? The real goal here is have well-defined data, something that I really think dictionaries just don't give me. namedtuples or objects with __slots__ seem to be better solutions. – Brian Lamar Aug 31 '11 at 20:59
  • @Brian: Indeed. You asked if there were any reasons to use a dictionary, and those are reasons. – zenWeasel Sep 01 '11 at 23:57
1

You could implement this nicely with namedtuple. For example, you could create a Widget namedtuple with default values:

>>> from collections import namedtuple
>>> _Widget = namedtuple("Widget", "propertyA propertyB propertyC propertyD propertyE propertyF propertyG propertyH propertyI")
>>> DefaultWidget = _Widget(None, 55, "Default", None, None, None, None, None, None)
>>> DefaultWidget
Widget(propertyA=None, propertyB=55, propertyC='Default', propertyD=None, propertyE=None, propertyF=None, propertyG=None, propertyH=None, propertyI=None)

Then, you can have a function called Widget that initializes the properties:

def Widget(propertyA, **kwargs):
   return DefaultWidget._replace(propertyA=propertyA, **kwargs)

Then you can use it like this:

>>> Widget("test", propertyE=17)
Widget(propertyA='test', propertyB=55, propertyC='Default', propertyD=None, propertyE=17, propertyF=None, propertyG=None, propertyH=None, propertyI=None)

Note that if you try to omit the required propertyA:

>>> Widget()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Widget() takes exactly 1 argument (0 given)

or if you give a property that doesn't exist:

>>> Widget("test", propertyZ="test2")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in Widget
  File "<string>", line 32, in _replace
ValueError: Got unexpected field names: ['propertyZ']

it handles it in a nice way. I think using namedtuple would get rid of your drawbacks about using a dictionary.

jterrace
  • 64,866
  • 22
  • 157
  • 202
  • Not really the question, but true that this is much nicer than doing it with just a dictionary. It's not particularly fast, though. – agf Aug 28 '11 at 23:39
  • Can you clarify why you think it's not fast? – jterrace Aug 28 '11 at 23:46
  • see [What is the fastest to access struct-like object in Python?](http://stackoverflow.com/questions/2646157/what-is-the-fastest-to-access-struct-like-object-in-python) – agf Aug 29 '11 at 00:31
  • If you're worried about small, constant factors, you're worried about the wrong thing. – jterrace Aug 29 '11 at 01:00
  • He mentioned speed in the question, so it's relevant. I still agree that `namedtuple` is better for his example than using a dictionary, but that wasn't really his question. – agf Aug 29 '11 at 01:05
0

My preference tends to be to use objects. My reasoning is that they are easier to extend. If people are accessing an object through fields, if additional functionality is needed those fields can become properties. If they are accessing keys, it is difficult to add additional logic without changing the interface.

freakTheMighty
  • 1,172
  • 1
  • 12
  • 27