开发者

Preferring dictionaries over objects in Python

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 cr开发者_如何学编程eate() 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?


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


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.


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.


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜