数据对象接受的样式是什么样的python

what is the accepted style python for data objects

本问题已经有最佳答案,请猛点这里访问。

Python中数据对象的正常样式是什么?假设我有一个方法从某个地方(net,db,…)获取客户,我应该返回什么类型的对象。我看到了几个选择:

  • 元组
  • 字典
  • 类实例(是数据类"normal")。

我相信还有其他人。做我的第一个大型python项目,所以我想从使用最佳实践开始

哇-对这个问题的负面反应感到惊讶。可能不清楚

我有很多不同的数据项想要传递我的代码。用户、产品、客户、订单……(事实上,它们不是那样的,而是简单的,有明显类型的东西)。所以我有

1
2
def get_user():
  return x

X应该是什么。一个名为user、dict、tuple的类实例…

没有与对象关联的方法,纯数据

似乎要走的路是两个名字

编辑:这个样式怎么样

1
2
3
4
5
6
7
8
9
10
11
class product:
   pass

...

def get_product():
   ... db read stuff
   pr = product()
   pr.name = dbthing[0]
   pr.price = dbthing[1]
   return pr

那是BARF诱导型的还是成熟型的还是古怪型的?它起作用了。从使用者的角度来看,它使代码可读

1
2
3
def xxx():
  pr = get_product()
  total = amount * pr.price


对于简单的数据记录,您通常应该首先考虑collections.namedtuple,如果出于任何原因不适合,则使用其他选项之一。一旦你知道为什么它不合适,通常会建议使用其他的哪一种。您可以将collections.namedtuple视为快速定义不可变"数据类"的快捷方式。

以数据库为例,如果您使用的是ORM,那么即使最简单的记录也将表示为对象[*],并且有很好的理由,因为它们都有一些共同的操作,您可以对它们执行,例如将更改存储回数据库。ORM的教程/文档将指导您。如果您直接使用python-db-api,那么来自SQL查询的行(最初)将作为元组返回,但当然,一旦您拥有了它们,就可以对它们执行您喜欢的操作。此外,数据库连接器还可以提供在调用execute()返回它们之前对它们进行操作的方法,例如在sqlite3中设置行工厂。

以"网络"为例——好吧,有许多数据交换的方法,但是一个常见的例子是访问返回JSON数据的API。在这种情况下,没有太多的选择,但是最初在程序中用与JSON结构相同的方式表示这些数据:作为包含列表、字典、字符串和数字的列表和字典。同样,一旦你拥有了它,你就可以用它做你喜欢做的事情。用它工作是很正常的,直接把它重组成其他东西也是很正常的。个人偏好和特殊情况都会影响你实际选择的对象。

你当然应该把所有这些都看作是可行的选择。一般来说,您将使用:

  • 当每个位置都有自己的意义时的元组。这就是为什么python通过返回元组从函数返回"多个值",因为不同的东西可能是具有不同含义的完全不同的类型。
  • 当可用键随记录而变化时的字典。
  • 当每个记录的键都相同时的数据类。对于不可变的记录,可以使用namedtuple
  • 当顺序很重要但位置都相等时的列表或元组。注意在"元组是不可变的,列表是可变的"和"元组用于异构数据,列表用于同构数据"之间有一点紧张。我个人倾向于前者,但我已经看到了后者的合理论据,所以如果你问人们一般是如何做出选择的,你不能忽视这一点。

[*]当然,元组和字典也是对象,我指的是这些数据结构之外的对象;-)


我将"数据对象"解释为一个不变的对象,通常有几个字段。

您经常看到的一个选项是只使用带有字段作为键的标准字典。但就我个人而言,我不喜欢这样,随着软件越来越大,很难确切地看到密钥的存在以及它们来自何处。人们开始编写向现有字典添加新键的函数,而这一切都变得一团糟。

我觉得你的空产品类有点奇怪——如果你要这样做,把值传递给构造函数,让它设置属性。然后,这是最普通的方法——一个带有一些属性的简单类,而没有其他属性。

但是命名副本更酷,因为它们是不可变的,所以当您阅读代码时,不必担心某些字段在某个地方发生了更改:

1
2
3
4
5
from collections import namedtuple

Product = namedtuple('Product', 'name price')

p = Product("some product", 10)

但是现在您要向它添加功能,比如返回产品描述和价格的__unicode__方法。现在您可以再次将其转换为普通类,构造函数使用这些相同的参数。但是,您也可以将一个namedDuple子类化:

1
2
3
class Product(namedtuple('Product', 'name price')):
    def __unicode__(self):
        return"{} (${})".format(self.name, self.price)

它仍然是不变的。这就是我在需要纯数据对象时所做的。如果您需要它成为一个可变的类,可以将其中一个属性设置为可变的,或者将其转换为具有相同接口的普通类。


无论是NamedUpples还是Classes都可以工作。它实际上取决于您还做了什么,以及您是否向其他人提供了一个API。类通常用于更友好的API,而且您可以放入更清晰的访问点。

至于最后的代码示例,最好让类在构造期间尽可能采用参数。这样,您就可以从"出生"返回一个真实的、可用的对象,而不是等待填充的空壳。同样,从API的角度来看,如果在没有设置几个值的情况下无法使用类,那么就不可能在没有设置这些值的情况下创建类的实例。否则,你最后会在一堆地方写"如果没有设置:那么失败"来保护东西。这在C++语言或Java语言中更为真实,但它仍然是一种很好的风格和操作模式。