Python:我应该使用类还是字典?

我有一个类只包含字段,没有方法,像这样:

class Request(object): def __init__(self, environ): self.environ = environ self.request_method = environ.get('REQUEST_METHOD', None) self.url_scheme = environ.get('wsgi.url_scheme', None) self.request_uri = wsgiref.util.request_uri(environ) self.path = environ.get('PATH_INFO', None) # ... 

这可以很容易地被翻译成字典。 这个类对于未来的添加更加灵活,并且可以使用__slots__快速。 那么使用dict会有什么好处呢? 字典会比上课快吗? 比带插槽的课程还快?

你为什么要把这个字典? 有什么优势? 如果你以后想要添加一些代码,会发生什么? 你的__init__代码去哪里?

类是捆绑相关的数据(通常是代码)。

字典是用于存储键值关系的,通常键的types都是相同的,所有的值也都是一种types。 偶尔他们可以用于绑定数据,当关键字/属性名称不是全部知道前面,但往往这是一个迹象表明你的devise有问题。

保持这一类。

使用字典,除非你需要一个类的额外机制。 你也可以使用一个名为namedtuple的混合方法:

 >>> from collections import namedtuple >>> request = namedtuple("Request", "environ request_method url_scheme") >>> request <class '__main__.Request'> >>> request.environ = "foo" >>> request.environ 'foo' 

这里的性能差异将是最小的,但如果字典不是更快,我会感到惊讶。

python 的类下面的字典。 你在类的行为上得到了一些开销,但是如果没有探查器,你将无法注意到它。 在这种情况下,我相信你会从课堂中受益,因为:

  • 你所有的逻辑生活在一个单一的function
  • 很容易更新和保持封装
  • 如果你稍后改变任何东西,你可以很容易地保持接口相同

我认为每个人的用法对我来说太主观了,所以我只是坚持数字。

我比较了创build和更改字典中的variables,new_style类和new_style类带槽的时间。

这是我用来testing它的代码(这有点乱,但它做的工作。)

 import timeit class Foo(object): def __init__(self): self.foo1 = 'test' self.foo2 = 'test' self.foo3 = 'test' def create_dict(): foo_dict = {} foo_dict['foo1'] = 'test' foo_dict['foo2'] = 'test' foo_dict['foo3'] = 'test' return foo_dict class Bar(object): __slots__ = ['foo1', 'foo2', 'foo3'] def __init__(self): self.foo1 = 'test' self.foo2 = 'test' self.foo3 = 'test' tmit = timeit.timeit print 'Creating...\n' print 'Dict: ' + str(tmit('create_dict()', 'from __main__ import create_dict')) print 'Class: ' + str(tmit('Foo()', 'from __main__ import Foo')) print 'Class with slots: ' + str(tmit('Bar()', 'from __main__ import Bar')) print '\nChanging a variable...\n' print 'Dict: ' + str((tmit('create_dict()[\'foo3\'] = "Changed"', 'from __main__ import create_dict') - tmit('create_dict()', 'from __main__ import create_dict'))) print 'Class: ' + str((tmit('Foo().foo3 = "Changed"', 'from __main__ import Foo') - tmit('Foo()', 'from __main__ import Foo'))) print 'Class with slots: ' + str((tmit('Bar().foo3 = "Changed"', 'from __main__ import Bar') - tmit('Bar()', 'from __main__ import Bar'))) 

这里是输出…

创build…

 Dict: 0.817466186345 Class: 1.60829183597 Class_with_slots: 1.28776730003 

更改variables…

 Dict: 0.0735140918748 Class: 0.111714198313 Class_with_slots: 0.10618612142 

所以,如果你只是存储variables,你需要速度,并不需要你做很多计算,我推荐使用一个字典(你总是可以做一个看起来像一个方法的函数)。 但是,如果你确实需要类,记住 – 总是使用__ slots __

注意:

我用new_style和old_style类testing了'Class'。 事实certificate,old_style类创build速度较快,但修改速度较慢(如果您在紧密的循环中创build大量类,则不会太多但意义重大(提示:您做错了))。

另外,由于我的老旧速度很慢,因此在计算机上创build和更改variables的时间可能会有所不同。 确保你自己testing看到“真实”的结果。

编辑:

我后来testing了namedtuple:我不能修改它,但创build10000个样本(或类似的东西)花了1.4秒,所以字典确实是最快的。

如果我改变字典function ,包括键和值,并返回字典,而不是包含字典的variables,当我创build它给我0.65而不是0.8秒。

 class Foo(dict): pass 

创build就像一个有插槽的类,改变variables是最慢的(0.17秒),所以不要使用这些类 。 去一个字典(速度)或从对象派生的类('语法糖果')

我同意@adw。 我不会用字典来表示一个“对象”(用OO的意思)。 字典汇总名称/值对。 类表示对象。 我已经看过用字典表示对象的代码,而且不清楚事物的实际形状是什么。 当某些名称/值不存在时会发生什么? 什么限制了客户端的设置,或者试图获取任何东西。 事物的形状应该总是明确的。

在使用Python时,必须严格遵守纪律,因为这种语言可以让作者在脚下自己拍摄。

我会推荐一个类,因为它是与请求有关的各种信息。 如果使用字典,我希望存储的数据本质上更加相似。 我倾向于遵循自己的指导方针是,如果我可能想循环遍历整个键 – >值对,并使用一个字典。 否则,数据显然比基本的键 – >值映射具有更多的结构,这意味着一个类可能是一个更好的select。

因此,坚持课堂。

也可能有你的蛋糕,也吃了。 换句话说,你可以创build一些提供类和字典实例function的东西。 查看ActiveState的字典与属性风格的访问配方和评论的方式。

如果你决定使用一个普通的类而不是子类,那么我发现一堆命名的东西 “类 ”配方的“ 简单而方便”的收集器非常灵活和有用的,重新做(即创build一个相对简单的信息聚合器)。 由于它是一个类,您可以稍后通过添加方法轻松扩展其function。

最后需要注意的是,类成员的名字必须是合法的Python标识符,但是字典关键字不会 – 所以字典会在这方面提供更大的自由度,因为关键字可以是任何可散列的(甚至不是string)。

如果所有你想要的都是语法糖果,比如obj.bla = 5而不是obj['bla'] = 5 ,特别是如果你不得不重复一遍,那么你可能需要使用一些普通的容器类。 尽pipe如此,代码相当臃肿,不必要的慢。 你可以保持简单:

 class AttrDict(dict): """ Syntax candy """ __getattr__ = dict.__getitem__ __setattr__ = dict.__setitem__ __delattr__ = dict.__delitem__ 

使用__slots__切换到namedtuple或类的另一个原因可能是内存使用情况。 字典需要比列表types更多的内存,所以这可能是一个需要思考的问题。

无论如何,在你的具体情况下,似乎没有任何动机离开你目前的实施。 您似乎无法维护数百万个这样的对象,因此不需要列表派生types。 而且它实际上包含了__init__中的一些function逻辑,所以你也不应该使用AttrDict