1

在 Python (3.2) 中实现类型安全字典的好方法是什么 - 一个只允许将特定类型的对象添加到自身的字典?

我自己有一个简单的解决方案:使用“addItem”方法围绕字典构建一个包装类,该方法在添加对象之前执行类型检查断言。看看是否有人有更好的东西。

4

5 回答 5

7

这里的 Pythonic 方法是只使用普通字典并只向其中添加特定类型的对象 - 不要试图强制执行限制,它不应该是必需的。


编辑:为了扩展我的论点,让我解释一下——你似乎认为编写好的代码需要类型安全。第一个问题是为什么?当然,类型安全会在编译时捕获一些错误,但根据我的经验,这些错误很少见,即使是最琐碎的测试也很容易捕获,而且通常很容易修复。

相比之下,最烦人、最难修复和最难测试的错误是合乎逻辑的错误,计算机根本无法发现。最好通过制作易于理解的可读代码来防止这些错误,从而使错误更加突出。动态类型通过减少代码的冗长性极大地帮助了这一点。您可能会争辩说,键入会使代码更容易阅读(因为您可以在使用它们时看到变量的类型),但是在动态语言中,这种事情是通过仔细命名来给出的——如果我命名一个变量seq,人们会假设这是一个序列,可以这样使用。根据我的经验,描述性命名和良好文档的结合使动态代码变得更好。

归根结底,语言中的类型安全是一个偏好问题,然而,Python 是一种围绕鸭子类型的思想设计的动态语言。该语言中的所有内容都是围绕这一点设计的,尝试以另一种方式使用它会适得其反。如果你想写Java,就写Java。

于 2013-01-07T11:34:53.623 回答
5

通过对,和进行子分类dict和添加警卫;添加一个从默认值中获取类型的类方法是一个很好的额外功能:__setitem__.update().setdefault().fromkeys()

from itertools import tee

class MyTypeDict(dict):
    def __init__(self, type_=SomeType, *args, **kw):
        self.type = type_
        super(MyTypeDict, self).__init__(*args, **kw)
        for val in self.itervalues():
            self._checktype(val)

    @classmethod
    def fromkeys(cls, seq, value=SomeType()):
        res = cls(type_=type(value))
        res.update((k, value) for k in seq)
        return res

    def _checktype(self, value):
        if not isinstance(value, self.type):
            raise TypeError('Value type {!r} not allowed'.format(type(value)))

    def __setitem__(self, key, value):
        self._checktype(value)
        super(MyTypeDict, self).__setitem__(key, value)

    def update(self, other):
        # Loop over other, either a dict or an iterable (use a copy with `tee`)
        # for python 3, use `items()` instead.
        items = other.iteritems() if hasattr(other, 'iteritems') else tee(other)
        for key, value in items:
            self._checktype(value)
        super(MyTypeDict, self).update(other)

    def setdefault(self, key, default=None):
        if default is None:
            default = self.type()  # assumes no-args initializer
        else:
            self._checktype(default)
        return super(MyTypeDict, self).setdefault(key, default)

将此用作:

mydict = MyTypeDict(type_=SomeType)
于 2013-01-07T11:39:41.820 回答
3

我认为您可以扩展字典并覆盖该__setitem__方法

class MyDict(dict):
    def __setitem__(self, key, val):
        #Test for proper interface
        if val.pass_the_test:
            dict.__setitem__(self, key, val)
        else:
            raise SomeKindOfException() 
于 2013-01-07T11:39:15.503 回答
2

Martijn 已经给了你一个答案,但正如你所看到的那样,正确处理极端情况是很棘手的。

如果您只想避免在脚上开枪,那么他的回答可能比您实际需要的要多;也许您只需要 wrap __setitem__,或者您最好让任何类型进入字典,但在完成添加内容或访问它们时进行某种断言。最后一个问题当然是 Python 人通常的回答:如果字典中的对象没有实现正确的接口,让代码在使用时中断,而不是预先检查。

另一方面,如果您需要防止恶意代码注入流氓值,Martijn 的代码是不够的;您可以通过以下方式轻松解决它:

dict.__setitem__(mydict, key, rogue_value)

此外,如果您真的打算将对象限制为单一类型,那么他的答案不是您想要的;你可能有一个通过isinstance测试但没有提供正确的鸭子打字行为的对象。

这就是为什么更多的问题背景会很有用。

于 2013-01-07T12:27:24.263 回答
0

根据DikeiMartijn Pieters的回答,我想出了这个简单的实现,足以满足我的需求:

class ETypedDictonaryException (Exception):
    pass

class TypedDict(dict):

    def __init__(self, keyType, valueType):
        dict.__init__(self)
        self.keyType = keyType
        self.valueType = valueType

    def __setitem__(self, key, value):
        if ( not isinstance(key, self.keyType) or not isinstance(value, self.valueType) ):
            raise  ETypedDictonaryException("wrong key type:" +str(self.keyType) + " and " +str(self.valueType)+ "  required!")
        dict.__setitem__(self, key, value)
于 2013-01-20T18:50:18.210 回答