【问题标题】:Preferring dictionaries over objects in Python在 Python 中优先使用字典而不是对象
【发布时间】:2011-08-28 22:49:43
【问题描述】:

当您所做的只是描述某物的属性时,在 Python 中使用字典而不是对象有什么好处(反之亦然)?

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

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

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)

当我看到这个时,我发现这些属性中的每一个都描述了一个“小部件”,并且想要执行以下操作:

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,这可以通过将小部件分解成更小的组件/相关部分并创建更多对象等来避免,但我觉得这是一个很好的“第一步”。这有任何意义吗?

字典的好处

  1. 更快和/或更节省内存

字典的缺点

  1. 无法使用静态代码检查器捕获某些错误
  2. 所有小部件属性的完整列表可能永远不会出现或不为人所知

对象的好处

  1. 确切了解“小部件”的组成部分
  2. 可能会使用静态代码检查器捕获错误(尽管使用 ** 魔法可以防止其中一些错误)

对象缺点

  1. 速度较慢和/或内存效率较低

这似乎是一个愚蠢的问题,但为什么要对可以用字典完成的对象做一些事情呢?

【问题讨论】:

  • 你考虑过namedtuple吗?
  • Do I need to learn about objects, or can I save time and just learn dictionaries? 和其他人的部分副本。只需搜索标记为 [python][oop] 的问题。
  • 也许你应该切换到 Javascript,其中对象是字典,这样整个问题就消失了 :-)
  • 另外,“对象”在这里并不正确(正如我对链接问题的回答所解释的那样)你真的是指用户定义的类。
  • “更快和/或更高的内存效率”和“更慢和/或更低的内存效率”是不合常理的谈话。如果你需要关心这些,你不应该使用 Python。所以现在你只剩下字典的缺点对象的好处了。 pythonic的选择现在应该很明显了

标签: python design-patterns object dictionary


【解决方案1】:

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

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

【讨论】:

  • 用户定义的类而不是字典。
【解决方案2】:

使用任何内置数据类型总能为您带来某些功能的优势,而且它的行为是其他程序员所熟知的。字典给了你一个充满内置方法的拳头,没有人会怀疑它是否是可迭代的。

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

【讨论】:

  • 字典是对象,重要的是不要掩盖这一点。
  • 这是一个很好的答案。在 OP 的示例中,Widget 继承自 object,而不是 dict。如果您希望 Widget 拥有 Python 字典的全部功能,则必须重新实现大量方法,或者从 dict 或其子类之一继承。
  • 如果我不需要对象是可迭代的或其他“拳头十足”的内置字典方法怎么办?这里的真正目标是拥有定义明确的数据,我真的认为字典不会给我。带有 slots 的命名元组或对象似乎是更好的解决方案。
  • @Brian:确实。你问是否有任何理由使用字典,这些都是理由​​。
【解决方案3】:

您可以使用namedtuple 很好地实现这一点。例如,您可以使用默认值创建一个名为元组的 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)

请注意,如果您尝试省略所需的属性A:

>>> 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 可以摆脱使用字典的缺点。

【讨论】:

  • 不是真正的问题,但确实这比只使用字典要好得多。不过,它并不是特别快。
  • 你能解释一下为什么你认为它不快吗?
  • 如果您担心小的、恒定的因素,那么您担心的是错误的事情。
  • 他在问题中提到了速度,所以它是相关的。我仍然同意 namedtuple 在他的例子中比使用字典更好,但这不是他的问题。
【解决方案4】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-12-07
    • 2017-10-07
    • 2019-10-04
    • 2014-07-26
    • 2020-10-21
    • 1970-01-01
    • 2019-03-05
    • 2010-10-02
    相关资源
    最近更新 更多