我应该使用课程还是字典?

时间:2010-10-28 16:44:36

标签: python oop class dictionary

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

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会有好处吗?一个dict会比一个班级快吗?并且比带有插槽的课程更快?

9 个答案:

答案 0 :(得分:36)

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

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

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

答案 1 :(得分:34)

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

  • 您的所有逻辑都存在于一个功能中
  • 很容易更新并保持封装
  • 如果您以后更改任何内容,则可以轻松保持界面相同

答案 2 :(得分:22)

为什么你要把它变成字典呢?有什么好处?如果您以后想要添加一些代码会发生什么?您的__init__代码会在哪里?

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

字典用于存储键值关系,其中键通常都是相同类型,并且所有值也是一种类型。有时候,当关键/属性名称并非都是预先知道时,它们对于捆绑数据很有用,但通常这表明你的设计出了问题。

保持这个课程。

答案 3 :(得分:19)

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

我比较了在dict,new_style类和带插槽的new_style类中创建和更改变量所花费的时间。

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

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

这是输出......

创建...

Dict: 0.817466186345
Class: 1.60829183597
Class_with_slots: 1.28776730003

更改变量......

Dict: 0.0735140918748
Class: 0.111714198313
Class_with_slots: 0.10618612142

所以,如果你只是存储变量,你需要速度,并且它不会要求你做很多计算,我建议使用dict(你总是可以做一个看起来像一个函数的函数方法)。但是,如果你真的需要课程,请记住 - 始终使用 __ 广告位 __

注意:

我测试过&#39; Class&#39;使用两个 new_style和old_style类。事实证明,old_style类创建速度更快但修改速度较慢(如果您在紧密循环中创建大量类(注意:您做错了),则不是很多但很重要)。

创建和更改变量的时间也可能因计算机的不同而有所不同,因为我的计算机已经过时且速度很慢。确保你自己测试一下,看看真实的&#39;结果

编辑:

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

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

class Foo(dict):
    pass

创建就像一个带槽的类,更改变量的速度最慢(0.17秒),因此不要使用这些类。寻找一个字典(速度)或从对象派生的类(&#39; syntax candy&#39;)

答案 4 :(得分:10)

我同意@adw。我永远不会用字典表示“对象”(在OO意义上)。字典聚合名称/值对。类表示对象。我已经看到了用字典表示对象的代码,并且不清楚事物的实际形状是什么。当某些名称/值不存在时会发生什么?是什么限制了客户完全放入任何东西。或者试图获得任何东西。应始终明确定义物体的形状。

使用Python时,重要的是建立纪律,因为语言允许作者用很多方式射击他/她自己。

答案 5 :(得分:5)

我会推荐一个课程,因为这是与请求有关的各种信息。如果一个人使用字典,我希望存储的数据本质上更相似。我倾向于遵循的一个指导原则是,如果我想循环遍历整个key-&gt;值对并执行某些操作,我会使用字典。否则,数据显然具有比基本键 - >值映射更多的结构,这意味着类可能是更好的选择。

因此,坚持上课。

答案 6 :(得分:3)

也许可以吃蛋糕并吃掉它。换句话说,您可以创建一些提供类和字典实例功能的东西。请参阅ActiveState的Dɪᴄᴛɪᴏɴᴀʀʏ ᴡɪᴛʜ ᴀᴛᴛʀɪʙᴜᴛᴇ-sᴛʏʟᴇ ᴀᴄᴄᴇss 配方以及对此方法的评论。

如果您决定使用常规类而不是子类,我发现Tʜᴇ sɪᴍᴘʟᴇ ʙᴜᴛ ʜᴀɴᴅʏ "ᴄᴏʟʟᴇᴄᴛᴏʀ ᴏғ ᴀ ʙᴜɴᴄʜ ᴏғ ɴᴀᴍᴇᴅ sᴛᴜғғ" ᴄʟᴀss食谱(由Alex Martelli提供)非常灵活且有用的东西看起来你正在做(即创建一个相对简单的信息聚合器)。由于它是一个类,因此您可以通过添加方法轻松扩展其功能。

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

<强>更新

一个类object(没有__dict__)子类名为SimpleNamespace(确实有一个)被添加到Python 3.3中,是另一种选择。

答案 7 :(得分:3)

如果你想要获得的所有内容都是obj.bla = 5而不是obj['bla'] = 5的语法糖果,特别是如果你必须重复那么多,你可能想要使用一些普通的容器类,如在martineaus建议中。然而,那里的代码相当臃肿,而且不必要地缓慢。你可以保持简单:

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

切换到namedtuple或具有__slots__的类的另一个原因可能是内存使用情况。 Dicts需要比列表类型更多的内存,所以这可能需要考虑。

无论如何,在您的具体情况下,似乎没有任何动机可以摆脱当前的实施。您似乎没有维护数百万这些对象,因此不需要列表派生类型。它实际上包含__init__中的一些功能逻辑,所以你也不应该使用AttrDict

答案 8 :(得分:-1)

$a = Get-Date

$fxtDelimiter="----------------------------FXT Daily Status--------------------------------------------"
$fxtData=(Get-Content -tail 2 G:\EmailActivity\Logs\FXTRekon.log) -split ': '
$fxtDownloaded="No of fxt files downloaded for the day"
$fxtFilesDownloaded="$($fxtDownloaded): $($a.ToShortDateString())- $($fxtData[2])"

$symphonyDelimiter="----------------------------Symphony Daily Status--------------------------------------------"

$symphonyData=(Get-Content -tail 5 G:\EmailActivity\Logs\SymphonyRekon.log) -split ': '
$symphonyZipFilesDownloaded="No of symphony zip files downloaded for the day"
$symphonyZipsDownloaded="$($symphonyZipFilesDownloaded): $($a.ToShortDateString())- $($symphonyData[2])"
$symphonySourceFilesProcessed="No of symphony files processed for the day"
$symphonyFilesProcessed="$($symphonySourceFilesProcessed): $($a.ToShortDateString())- $($symphonyData[5])"

$MailBody =  $fxtDelimiter+"`n"+$fxtFilesDownloaded +"`n" + "`n" +$symphonyDelimiter+ "`n" +"`n" +$symphonyZipsDownloaded + "`n"+$symphonyFilesProcessed

ConvertTo-Html -Title "DailyStatus" -Body "<center><H2><u> Daily Feeds Report</u></H2></center>"  -PostContent $MailBody > G:\EmailActivity\Test.htm
 
Invoke-Expression G:\EmailActivity\Test.htm

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