在 Python (3.2) 中实现类型安全字典的好方法是什么 - 一个只允许将特定类型的对象添加到自身的字典?
我自己有一个简单的解决方案:使用“addItem”方法围绕字典构建一个包装类,该方法在添加对象之前执行类型检查断言。看看是否有人有更好的东西。
在 Python (3.2) 中实现类型安全字典的好方法是什么 - 一个只允许将特定类型的对象添加到自身的字典?
我自己有一个简单的解决方案:使用“addItem”方法围绕字典构建一个包装类,该方法在添加对象之前执行类型检查断言。看看是否有人有更好的东西。
这里的 Pythonic 方法是只使用普通字典并只向其中添加特定类型的对象 - 不要试图强制执行限制,它不应该是必需的。
编辑:为了扩展我的论点,让我解释一下——你似乎认为编写好的代码需要类型安全。第一个问题是为什么?当然,类型安全会在编译时捕获一些错误,但根据我的经验,这些错误很少见,即使是最琐碎的测试也很容易捕获,而且通常很容易修复。
相比之下,最烦人、最难修复和最难测试的错误是合乎逻辑的错误,计算机根本无法发现。最好通过制作易于理解的可读代码来防止这些错误,从而使错误更加突出。动态类型通过减少代码的冗长性极大地帮助了这一点。您可能会争辩说,键入会使代码更容易阅读(因为您可以在使用它们时看到变量的类型),但是在动态语言中,这种事情是通过仔细命名来给出的——如果我命名一个变量seq
,人们会假设这是一个序列,可以这样使用。根据我的经验,描述性命名和良好文档的结合使动态代码变得更好。
归根结底,语言中的类型安全是一个偏好问题,然而,Python 是一种围绕鸭子类型的思想设计的动态语言。该语言中的所有内容都是围绕这一点设计的,尝试以另一种方式使用它会适得其反。如果你想写Java,就写Java。
通过对,和进行子分类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)
我认为您可以扩展字典并覆盖该__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()
Martijn 已经给了你一个答案,但正如你所看到的那样,正确处理极端情况是很棘手的。
如果您只想避免在脚上开枪,那么他的回答可能比您实际需要的要多;也许您只需要 wrap __setitem__
,或者您最好让任何类型进入字典,但在完成添加内容或访问它们时进行某种断言。最后一个问题当然是 Python 人通常的回答:如果字典中的对象没有实现正确的接口,让代码在使用时中断,而不是预先检查。
另一方面,如果您需要防止恶意代码注入流氓值,Martijn 的代码是不够的;您可以通过以下方式轻松解决它:
dict.__setitem__(mydict, key, rogue_value)
此外,如果您真的打算将对象限制为单一类型,那么他的答案不是您想要的;你可能有一个通过isinstance
测试但没有提供正确的鸭子打字行为的对象。
这就是为什么更多的问题背景会很有用。
根据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)