优先使用Python中的对象字典

时间:2011-08-28 22:49:43

标签: python design-patterns object dictionary

当你所做的只是描述某些属性时,使用字典而不是Python中的对象(反之亦然)会有好处吗?

我正在处理的项目目前有许多地方使用字典,我通常会创建对象。在我看来,对象提供了更多的结构,并允许通过诸如pylint之类的程序进行更好的程序员错误检查,但是很难解释为什么我会使用对象而不是dict。

对于模拟示例,一个模块创建Widgets并包含如下方法:

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

通过创建一个字典并将其传递给它来调用该方法:

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

当我看到这个时,我发现这些属性中的每一个都是描述'Widget'并且想要执行以下操作:

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)

然后更新create()方法,看起来像这样:

def create(self, widget):

最终被称为这样:

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

在我看来,这显然更好,但过去我错了,我想不出如何解释自己。当然我还在使用** kwargs可以通过将Widget分解为更小的组件/相关部件并创建更多对象等来避免,但我觉得这是一个很好的“第一步”。这有什么意义吗?

字典优惠

  1. 更快和/或更高效的内存
  2. 字典缺点

    1. 无法通过静态代码检查程序捕获一些错误
    2. 可能永远不会显示或知道所有窗口小部件属性的完整列表
    3. 对象优势

      1. 确切知道'Widget'是由
      2. 组成的
      3. 使用静态代码检查程序可能会捕获错误(尽管使用** magic会阻止其中的一些)
      4. 对象缺点

        1. 较慢和/或内存效率较低
        2. 这似乎是一个愚蠢的问题,但为什么要使用可以用词典完成的对象呢?

4 个答案:

答案 0 :(得分:2)

不,使用字典而不是对象没有任何好处 - 对象中的数据通常存储在字典中。

使用对象而不是字典可能会有好处。看到: http://docs.python.org/reference/datamodel.html#slots

答案 1 :(得分:2)

使用任何内置数据类型总能为您提供某些功能的优势,而且其行为为其他程序员所熟知。一本字典为你提供了一个完整的内置方法,没有人不知道它是否可以迭代。

这只是一个优点。并不是说我应该总是使用字典而不是声明自己的对象。 (当然,您的新对象可以继承类似字典的行为)但是,当更简单的存储机制可能时,您不一定总是选择创建新对象。以理解为指导,取决于Widget是否有任何特殊行为或属性。

答案 2 :(得分:1)

您可以使用namedtuple很好地实现此功能。例如,您可以创建一个名为widget的Widget,其默认值为:

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

然后,您可以使用一个名为Widget的函数初始化属性:

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

然后你可以像这样使用它:

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

请注意,如果您尝试省略所需的propertyA:

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

或者如果您提供不存在的财产:

>>> 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']

它以一种很好的方式处理它。我认为使用namedtuple可以摆脱使用字典的缺点。

答案 3 :(得分:0)

我的偏好倾向于使用对象。我的理由是它们更容易扩展。如果人们通过字段访问对象,则如果需要其他功能,则这些字段可以成为属性。如果他们正在访问密钥,则很难在不更改接口的情况下添加其他逻辑。