键入安全的Python 3.2字典

时间:2013-01-07 11:29:50

标签: python types dictionary

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

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

5 个答案:

答案 0 :(得分:7)

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


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

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

当谈到它时,在语言中键入安全性是一个优先考虑的问题,然而,Python是一种围绕鸭子打字概念设计的动态语言。语言中的所有内容都是围绕着这种方式设计的,并且尝试以另一种方式使用它将极其适得其反。如果你想编写Java,请编写Java。

答案 1 :(得分: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)

答案 2 :(得分: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() 

答案 3 :(得分:2)

Martijn已经给你一个答案了,但是你可以看到正确处理角落案件很棘手。

如果你想要的只是避免在脚下射击,那么他的答案可能比你实际需要的更多;也许你只需要包装__setitem__,或者你最好让任何类型进入字典,但在你完成添加或访问它们时做某种断言。当然,最后一个是Python人员的常见答案:如果字典中的对象没有实现正确的接口,那么在使用代码时代码会中断,而不是在前面检查。

另一方面,如果你需要防止恶意代码注入流氓价值,Martijn的代码就不够了;你可以通过致电:

轻松解决这个问题
dict.__setitem__(mydict, key, rogue_value)

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

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

答案 4 :(得分:0)

基于 Dikei Martijn 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)