Python 我应该使用课堂还是字典?

Python 我应该使用课堂还是字典?,python,oop,class,dictionary,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) s

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

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。该类对于将来的添加更加灵活,并且可以使用
\uuuuuu插槽\uuuuu
实现快速。那么使用dict会有好处吗?听写会比上课快吗?而且比带插槽的类更快?

除非需要类的额外机制,否则请使用字典。您也可以使用一个混合方法:

>>> from collections import namedtuple
>>> request = namedtuple("Request", "environ request_method url_scheme")
>>> request
<class '__main__.Request'>
>>> request.environ = "foo"
>>> request.environ
'foo'
>>从集合导入名为tuple的
>>>request=namedtuple(“request”、“environrequest\u方法url\u方案”)
>>>请求
>>>request.environ=“foo”
>>>请求环境
“福”

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

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

  • 所有逻辑都存在于一个函数中
  • 它易于更新并保持封装状态
  • 如果以后更改了任何内容,可以很容易地保持界面不变

我推荐一个类,因为它是与请求相关的各种信息。如果使用字典,我希望存储的数据在性质上更相似。我倾向于遵循的一条准则是,如果我想循环整个键->值对集合并做一些事情,我会使用字典。否则,数据显然比基本的键->值映射具有更多的结构,这意味着类可能是更好的选择


因此,坚持上课。

你为什么要把这本书变成字典?有什么好处?如果以后要添加一些代码,会发生什么?你的
\uuuu init\uuuu
代码会去哪里

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

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


保持这一类。

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


在使用Python时,遵守规则是很重要的,因为该语言允许作者通过多种方式攻击自己。

也可以吃蛋糕。换句话说,您可以创建同时提供类和字典实例功能的东西。请参阅ActiveState的配方和方法说明

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

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

更新


Python3.3模块中添加了一个名为(确实有一个)的类(没有
\u dict\u
)子类,这是另一种选择。

如果您想要实现的只是语法糖果,比如
obj.bla=5
,而不是
obj['bla']=5
,特别是如果您必须重复多次,您可能希望使用一些简单的容器类,如martineaus建议中所述。然而,那里的代码非常臃肿,速度慢得不必要。你可以这样简单:

class AttrDict(dict):
    """ Syntax candy """
    __getattr__ = dict.__getitem__
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__
切换到
namedtuple
s或具有
插槽的类的另一个原因可能是内存使用。Dicts比列表类型需要更多的内存,所以这可能是一个值得考虑的问题


无论如何,在您的特定情况下,似乎没有任何动机来改变您当前的实现。您似乎没有维护数百万个这样的对象,因此不需要列表派生类型。它实际上在
\uuu init\uuuu
中包含了一些函数逻辑,所以你也不应该使用
AttrDict
,我认为每个函数的用法都太主观了,我无法理解,所以我还是坚持数字

我比较了创建和更改dict中的变量、新的\u样式类和带有插槽的新\u样式类所需的时间

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

这是输出

创建。。。 正在更改变量。。。 所以,如果你只是存储变量,你需要速度,而且不需要你做很多计算,我建议你使用dict(你总是可以做一个看起来像方法的函数)。但是,如果您确实需要类,请记住-始终使用\uuuuuuu插槽\uuuuu

注: 我用新的和旧的类来测试“类”。事实证明,旧的_风格的类创建速度更快,但修改速度较慢(如果您在一个紧密的循环中创建了大量的类,则不会有太多,但意义重大(提示:您做错了))

另外,由于我的电脑又旧又慢,所以在你的电脑上创建和更改变量的时间可能会有所不同。文科硕士
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
class Foo(dict):
    pass
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)