在Python(3.2)中实现类型安全字典的好方法是什么 - 只允许将特定类型的对象添加到自身的字典?
我自己有一个简单的解决方案:使用'addItem'方法在字典周围构建一个包装类,在添加对象之前执行类型检查断言。想看看有人有更好的东西。
答案 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)