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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4045161/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 13:59:43  来源:igfitidea点击:

Should I use a class or dictionary?

pythonoopclassdictionary

提问by deamon

I have a class that contains only fields and no methods, like this:

我有一个只包含字段而没有方法的类,如下所示:

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)
        # ...

This could easily be translated to a dict. The class is more flexible for future additions and could be fast with __slots__. So would there be a benefit of using a dict instead? Would a dict be faster than a class? And faster than a class with slots?

这可以很容易地转换为字典。该类对于未来的添加更加灵活,并且可以快速使用__slots__. 那么使用 dict 会有什么好处吗?dict 会比类更快吗?并且比带插槽的班级更快?

采纳答案by adw

Why would you make this a dictionary? What's the advantage? What happens if you later want to add some code? Where would your __init__code go?

你为什么要把它做成字典?有什么好处?如果您以后想添加一些代码会怎样?你的__init__代码会去哪里?

Classes are for bundling related data (and usually code).

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

Dictionaries are for storing key-value relationships, where usually the keys are all of the same type, and all the values are also of one type. Occasionally they can be useful for bundling data when the key/attribute names are not all known up front, but often this a sign that something's wrong with your design.

字典是用来存储键值关系的,通常键都是同一种类型,所有的值也是一种类型。有时,当键/属性名称不是全部已知时,它们对于捆绑数据很有用,但这通常表明您的设计有问题。

Keep this a class.

保持这个类。

回答by Katriel

Use a dictionary unless you need the extra mechanism of a class. You could also use a namedtuplefor a hybrid approach:

除非您需要类的额外机制,否则请使用字典。您还可以将 anamedtuple用于混合方法:

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

Performance differences here will be minimal, although I would be surprised if the dictionary wasn't faster.

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

回答by Bruce Armstrong

A class in python isa dict underneath. You do get some overhead with the class behavior, but you won't be able to notice it without a profiler. In this case, I believe you benefit from the class because:

python的类下面的字典。类行为确实会带来一些开销,但是如果没有分析器,您将无法注意到它。在这种情况下,我相信您从课程中受益,因为:

  • All your logic lives in a single function
  • It is easy to update and stays encapsulated
  • If you change anything later, you can easily keep the interface the same
  • 你所有的逻辑都存在于一个函数中
  • 易于更新并保持封装状态
  • 如果您稍后更改任何内容,您可以轻松地保持界面不变

回答by Stigma

I would recommend a class, as it is all sorts of information involved with a request. Were one to use a dictionary, I'd expect the data stored to be far more similar in nature. A guideline I tend to follow myself is that if I may want to loop over the entire set of key->value pairs and do something, I use a dictionary. Otherwise, the data apparently has far more structure than a basic key->value mapping, meaning a class would likely be a better alternative.

我会推荐一个类,因为它涉及请求的各种信息。如果使用字典,我希望存储的数据在本质上更加相似。我自己倾向于遵循的一个准则是,如果我可能想遍历整个键-> 值对集并执行某些操作,我会使用字典。否则,数据显然比基本的键->值映射具有更多的结构,这意味着类可能是更好的选择。

Hence, stick with the class.

因此,坚持上课。

回答by jaydel

I agree with @adw. I would never represent an "object" (in an OO sense) with a dictionary. Dictionaries aggregate name/value pairs. Classes represent objects. I've seen code where the objects are represented with dictionaries and it's unclear what the actual shape of the thing is. What happens when certain name/values aren't there? What restricts the client from putting anything at all in. Or trying to get anything at all out. The shape of the thing should always be clearly defined.

我同意@adw。我永远不会用字典表示一个“对象”(在面向对象的意义上)。字典聚合名称/值对。类代表对象。我见过用字典表示对象的代码,但不清楚事物的实际形状是什么。当某些名称/值不存在时会发生什么?是什么限制了客户把任何东西放进去。或者试图把任何东西拿出来。事物的形状应始终明确定义。

When using Python it is important to build with discipline as the language allows many ways for the author to shoot him/herself in the foot.

使用 Python 时,必须遵守纪律,因为该语言允许作者以多种方式向自己开枪。

回答by martineau

It may be possible to have your cake and eat it, too. In other words you can create something that provides the functionality of both a class and dictionary instance. See the ActiveState's D????????? ???? ?????????-s???? ????ss recipe and comments on ways of doing that.

也有可能拥有你的蛋糕并吃掉它。换句话说,您可以创建提供类和字典实例功能的东西。看到 ActiveState 的D????????? ????????????-s???? ????ss食谱和评论方法。

If you decide to use a regular class rather than a subclass, I've found the T?? s????? ??? ????? "????????? ?? ? ????? ?? ????? s????" ???ssrecipe (by Alex Martelli) to be veryflexible and useful for the sort of thing it looks like you're doing (i.e. create a relative simple aggregator of information). Since it's a class you can easily extend its functionality further by adding methods.

如果您决定使用常规类而不是子类,我已经找到了T?? ????? ???????? “???????????????????????????ssrecipe(由 Alex Martelli 编写)对于您正在做的事情(即创建一个相对简单的信息聚合器)非常灵活和有用。由于它是一个类,因此您可以通过添加方法轻松地进一步扩展其功能。

Lastly it should be noted that the names of class members must be legal Python identifiers, but dictionary keys do not—so a dictionary would provide greater freedom in that regard because keys can be anything hashable (even something that's not a string).

最后应该注意的是,类成员的名称必须是合法的 Python 标识符,但字典键不是——因此字典将在这方面提供更大的自由,因为键可以是任何可散列的(甚至不是字符串的东西)。

Update

更新

A class object(which doesn't have a __dict__) subclass named SimpleNamespace(which does have one) was added to the typesmodule Python 3.3, and is yet another alternative.

一类object(其不具有__dict__)指定的子类SimpleNamespace(它有一个)加入到该types模块的Python 3.3,并且是又一替代。

回答by Michael

If all that you want to achive is syntax candy like obj.bla = 5instead of obj['bla'] = 5, especially if you have to repeat that a lot, you maybe want to use some plain container class as in martineaus suggestion. Nevertheless, the code there is quite bloated and unnecessarily slow. You can keep it simple like that:

如果您想要实现的只是语法糖果obj.bla = 5而不是obj['bla'] = 5,特别是如果您必须重复很多,那么您可能想要使用一些简单的容器类,如 martineaus 建议。然而,那里的代码非常臃肿并且不必要地慢。你可以保持简单:

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

Another reason to switch to namedtuples or a class with __slots__could be memory usage. Dicts require significantly more memory than list types, so this could be a point to think about.

切换到namedtuples 或类的另一个原因__slots__可能是内存使用。字典需要比列表类型多得多的内存,所以这可能是一个值得考虑的问题。

Anyways, in your specific case, there doesn't seem to be any motivation to switch away from your current implementation. You don't seem to maintain millions of these objects, so no list-derived-types required. And it's actually containing some functional logic within the __init__, so you also shouldn't got with AttrDict.

无论如何,在您的特定情况下,似乎没有任何动力放弃当前的实现。您似乎没有维护数百万个这些对象,因此不需要列表派生类型。而它实际上包含内的一些功能逻辑__init__,所以你也不要带了AttrDict

回答by alexpinho98

I think that the usage of each one is way too subjective for me to get in on that, so i'll just stick to numbers.

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

I compared the time it takes to create and to change a variable in a dict, a new_style class and a new_style class with slots.

我比较了在 dict、new_style 类和带槽的 new_style 类中创建和更改变量所需的时间。

Here's the code i used to test it(it's a bit messy but it does the job.)

这是我用来测试它的代码(它有点凌乱,但它可以完成工作。)

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')))

And here is the output...

这是输出...

Creating...

创建...

Dict: 0.817466186345
Class: 1.60829183597
Class_with_slots: 1.28776730003

Changing a variable...

改变一个变量...

Dict: 0.0735140918748
Class: 0.111714198313
Class_with_slots: 0.10618612142

So, if you're just storing variables, you need speed, and it won't require you to do many calculations, i recommend using a dict(you could always just make a function that looks like a method). But, if you really need classes, remember - always use __slots__.

所以,如果你只是存储变量,你需要速度,而且不需要你做很多计算,我建议使用 dict(你总是可以制作一个看起来像方法的函数)。但是,如果您真的需要类,请记住 - 始终使用____

Note:

笔记:

I tested the 'Class' with bothnew_style and old_style classes. It turns out that old_style classes are faster to create but slower to modify(not by much but significant if you're creating lots of classes in a tight loop (tip: you're doing it wrong)).

我测试的“类”有两种new_style和old_style类。事实证明,old_style 类的创建速度较快,但修改速度较慢(如果您在紧密循环中创建大量类,则不会太多但很重要(提示:您做错了))。

Also the times for creating and changing variables may differ on your computer since mine is old and slow. Make sure you test it yourself to see the 'real' results.

此外,创建和更改变量的时间在您的计算机上可能会有所不同,因为我的计算机又旧又慢。确保您自己测试以查看“真实”结果。

Edit:

编辑:

I later tested the namedtuple: i can't modify it but to create the 10000 samples (or something like that) it took 1.4 seconds so the dictionary is indeed the fastest.

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

If i change the dict functionto include the keys and values and to return the dict instead of the variable containing the dict when i create it it gives me 0.65 instead of 0.8 seconds.

如果我更改 dict 函数以包含键和值,并在创建它时返回 dict 而不是包含 dict 的变量,它会给我0.65 而不是 0.8 秒。

class Foo(dict):
    pass

Creating is like a class with slots and changing the variable is the slowest (0.17 seconds) so do not use these classes. go for a dict (speed) or for the class derived from object ('syntax candy')

创建就像一个带有插槽的类,更改变量是最慢的(0.17 秒),所以不要使用这些类。选择 dict(速度)或从对象派生的类('syntax candy')

回答by LowQualityDelivery

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)

I recommend a class. If you use a class, you can get type hint as shown. And Class support auto complete when class is argument of function.

我推荐一门课。如果您使用类,您可以获得如图所示的类型提示。当类是函数的参数时,类支持自动完成。

enter image description here

在此处输入图片说明