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 が答えをくれましたが、おわかりのように、コーナー ケースを正しく理解するのは難しいものです。
あなたが自分の足を撃たないようにしたいだけなら、彼の答えはあなたが実際に必要としている以上のものかもしれません。おそらく、ラップするだけでよい__setitem__
か、または、任意の型を辞書に入れる方がよいでしょうが、追加が完了したとき、またはそれらにアクセスしたときに何らかのアサーションを実行する方がよいでしょう。これらの最後のものはもちろん、Python 関係者からの通常の回答です。ディクショナリ内のオブジェクトが正しいインターフェイスを実装していない場合、前もってチェックするのではなく、使用時にコードを壊してください。
一方、不正な値を挿入する悪意のあるコードから保護する必要がある場合、Martijn のコードでは不十分です。次のように呼び出すことで、簡単に回避できます。
dict.__setitem__(mydict, key, rogue_value)
また、オブジェクトを 1 つのタイプに制限することを本当に意図している場合、彼の答えはあなたが望んでいたものではありません。isinstance
テストには合格したが、正しいダックタイピング動作を提供しなかったオブジェクトを作成できます。
そのため、質問に対するより多くのコンテキストが役立ちます。
DikeiとMartijn Pietersからの回答 here に基づいて、この単純な実装を思いつきました。これは私のニーズに十分です。
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)