字典与对象 - 哪个更高效,为什么?

150 投票
8 回答
85264 浏览
提问于 2025-04-15 13:53

在Python中,字典和对象在内存使用和CPU消耗方面,哪个更高效呢?

背景:
我需要把大量数据加载到Python中。我创建了一个对象,用来存放字段。创建400万个实例并把它们放进一个字典里,花了大约10分钟,使用了大约6GB的内存。字典准备好后,访问它的速度非常快。

示例:
为了检查性能,我写了两个简单的程序,它们做的事情是一样的,一个使用对象,另一个使用字典:

对象(执行时间大约18秒):

class Obj(object):
  def __init__(self, i):
    self.i = i
    self.l = []
all = {}
for i in range(1000000):
  all[i] = Obj(i)

字典(执行时间大约12秒):

all = {}
for i in range(1000000):
  o = {}
  o['i'] = i
  o['l'] = []
  all[i] = o

问题:
我是不是做错了什么,还是说字典就是比对象快?如果字典确实表现更好,能有人解释一下为什么吗?

8 个回答

11

你有没有考虑过使用命名元组?(这是针对Python 2.4/2.5的链接

命名元组是一种新的标准方式来表示结构化数据,它结合了元组的高效性能和类的便利性。

它和字典相比唯一的缺点是(就像元组一样),创建后不能更改属性。

17

在一个对象中访问属性,其实是在后台使用字典的方式来实现的。所以,当你用属性访问时,其实是在增加一些额外的开销。而且在对象的情况下,还会因为一些额外的内存分配和代码执行(比如执行 __init__ 方法)而产生更多的开销。

在你的代码中,如果 o 是一个 Obj 的实例,那么 o.attro.__dict__['attr'] 是等价的,只是多了一点点额外的开销。

183

你有没有试过用 __slots__ 呢?

根据文档

默认情况下,无论是旧式类还是新式类的实例,都会有一个字典来存储属性。这对于那些只有很少实例变量的对象来说,会浪费空间。当创建大量实例时,这种空间浪费会变得很严重。

通过在新式类定义中定义 __slots__,可以覆盖这个默认设置。__slots__ 声明了一系列实例变量,并为每个实例保留足够的空间来存储每个变量的值。这样可以节省空间,因为每个实例不会创建 __dict__

那么,这样做是节省时间还是内存呢?

在我的电脑上比较了三种方法:

test_slots.py:

class Obj(object):
  __slots__ = ('i', 'l')
  def __init__(self, i):
    self.i = i
    self.l = []
all = {}
for i in range(1000000):
  all[i] = Obj(i)

test_obj.py:

class Obj(object):
  def __init__(self, i):
    self.i = i
    self.l = []
all = {}
for i in range(1000000):
  all[i] = Obj(i)

test_dict.py:

all = {}
for i in range(1000000):
  o = {}
  o['i'] = i
  o['l'] = []
  all[i] = o

test_namedtuple.py(在2.6中支持):

import collections

Obj = collections.namedtuple('Obj', 'i l')

all = {}
for i in range(1000000):
  all[i] = Obj(i, [])

运行基准测试(使用 CPython 2.5):

$ lshw | grep product | head -n 1
          product: Intel(R) Pentium(R) M processor 1.60GHz
$ python --version
Python 2.5
$ time python test_obj.py && time python test_dict.py && time python test_slots.py 

real    0m27.398s (using 'normal' object)
real    0m16.747s (using __dict__)
real    0m11.777s (using __slots__)

使用 CPython 2.6.2,包括命名元组测试:

$ python --version
Python 2.6.2
$ time python test_obj.py && time python test_dict.py && time python test_slots.py && time python test_namedtuple.py 

real    0m27.197s (using 'normal' object)
real    0m17.657s (using __dict__)
real    0m12.249s (using __slots__)
real    0m12.262s (using namedtuple)

所以是的(这并不意外),使用 __slots__ 是一种性能优化。使用命名元组的性能与 __slots__ 相似。

撰写回答