Should I use a class or dictionary?
我有一个类,它只包含字段,不包含方法,如下所示:
1 2 3 4 5 6 7 8 9 | 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) # ... |
这可以很容易地翻译成dict。该类对于将来的添加更为灵活,并且可以与
除非需要类的额外机制,否则使用字典。您也可以使用
1 2 3 4 5 6 7 | >>> from collections import namedtuple >>> request = namedtuple("Request","environ request_method url_scheme") >>> request <class '__main__.Request'> >>> request.environ ="foo" >>> request.environ 'foo' |
这里的性能差异是最小的,尽管如果字典速度不快,我会感到惊讶。
python中的类是下面的dict。类行为确实会带来一些开销,但是如果没有分析器,您将无法注意到它。在这种情况下,我相信你会从课堂中受益,因为:
- 你所有的逻辑都活在一个功能中
- 它很容易更新并保持封装
- 如果以后再更改任何内容,您可以轻松地保持界面不变。
你到底为什么要把这个编成字典?有什么优势?如果您以后想添加一些代码,会发生什么?你的
类用于绑定相关数据(通常是代码)。
字典用于存储键值关系,通常键都属于同一类型,所有值也属于同一类型。有时,当键/属性名称不都是预先知道的时,它们可以用于捆绑数据,但这通常是您的设计有问题的迹象。
把这当作一门课。
我觉得每一个的用法对我来说都太主观了,我不想插手,所以我还是坚持数字。
我比较了在dict、一个新的_样式类和一个带槽的新_样式类中创建和更改变量所需的时间。
这是我用来测试它的代码(虽然有点乱,但它确实起到了作用)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | 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... ' 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 ' Changing a variable... ' 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'))) |
这里是输出…
创建。。。1 2 3 | Dict: 0.817466186345 Class: 1.60829183597 Class_with_slots: 1.28776730003 |
正在更改变量…
1 2 3 | Dict: 0.0735140918748 Class: 0.111714198313 Class_with_slots: 0.10618612142 |
所以,如果你只是在存储变量,你需要速度,而且不需要你做很多计算,我建议你使用dict(你总是可以做一个看起来像方法的函数)。但是,如果你真的需要课程,记住-总是使用槽。
注:我用新的和旧的"类"来测试"类"。原来,旧的_样式的类创建速度更快,修改速度较慢(如果您在紧密循环中创建了许多类,那么这一点不重要(提示:您做错了))。
另外,在你的计算机上创建和更改变量的时间可能会有所不同,因为我的变量既旧又慢。确保你自己测试它以看到"真实"的结果。
编辑:后来我测试了这个名字:我不能修改它,但是要创建10000个样本(或类似的东西),需要1.4秒,所以字典确实是最快的。
如果我将dict函数更改为包含键和值,并在创建dict时返回dict而不是包含dict的变量,那么它会给我0.65秒而不是0.8秒。
1 2 | class Foo(dict): pass |
创建就像一个带有槽的类,而更改变量是最慢的(0.17秒),所以不要使用这些类。选择dict(speed)或从object派生的类("syntax candy")。
我同意@adw。我绝不会用字典来表示一个"对象"(在OO意义上)。字典聚合名称/值对。类表示对象。我见过用字典表示物体的代码,不清楚物体的实际形状。当某些名称/值不存在时会发生什么?什么限制了客户将任何东西放入。或者想把任何东西弄出来。事物的形状应该总是清晰的定义。
当使用Python时,使用规则进行构建是很重要的,因为语言允许作者通过多种方式向自己的脚开枪。
我会推荐一个类,因为它是与请求相关的各种信息。如果要使用字典,我希望存储的数据在本质上更相似。我自己倾向于遵循的一条准则是,如果我想循环遍历整组键-值对并做一些事情,我就使用字典。否则,数据显然比基本的键-值映射具有更多的结构,这意味着类可能是更好的选择。
因此,坚持上课。
如果你想实现的只是像
1 2 3 4 5 | class AttrDict(dict): """ Syntax candy""" __getattr__ = dict.__getitem__ __setattr__ = dict.__setitem__ __delattr__ = dict.__delitem__ |
切换到
不管怎样,在您的特定情况下,似乎没有任何动机可以从您当前的实现中切换出来。您似乎没有维护数以百万计的这些对象,因此不需要列表派生类型。它实际上包含了
也可以吃你的蛋糕。换句话说,您可以创建同时提供类和字典实例功能的东西。看到活动区了吗???????????????????????????????-S???????????SS配方和对方法的评论。
如果您决定使用常规类而不是子类,那么我找到了t?S??????????????????"????????????????????????????????????S????"?"???SS配方(由Alex Martelli设计)非常灵活,对你正在做的事情非常有用(即创建一个相对简单的信息聚合器)。因为它是一个类,您可以通过添加方法轻松地进一步扩展它的功能。
最后,应该注意的是,类成员的名称必须是合法的python标识符,但是字典键没有这样做,因此字典在这方面提供了更大的自由度,因为键可以是任何可哈希的(即使是不是字符串的东西)。
更新
在python 3.3中添加了一个名为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class ClassWithSlotBase: __slots__ = ('a', 'b',) def __init__(self): self.a: str ="test" self.b: float = 0.0 def test_type_hint(_b: float) -> None: print(_b) class_tmp = ClassWithSlotBase() test_type_hint(class_tmp.a) |
我推荐一门课。如果使用类,则可以获取类型提示,如图所示。当类是函数的参数时,类支持自动完成。