Pythondict
は非常に便利なデータ構造です。
d = {'a': 1, 'b': 2}
d['a'] # get 1
値でインデックスを作成したい場合もあります。
d[1] # get 'a'
このデータ構造を実装するための最も効率的な方法はどれですか?それを行うための公式の推奨方法はありますか?
Pythondict
は非常に便利なデータ構造です。
d = {'a': 1, 'b': 2}
d['a'] # get 1
値でインデックスを作成したい場合もあります。
d[1] # get 'a'
このデータ構造を実装するための最も効率的な方法はどれですか?それを行うための公式の推奨方法はありますか?
{1: ['a', 'A'], 2: 'b'}
ます。そのような方法については、私の答えを参照してください。
回答:
これは、Python辞書の値からキーを検索dict
することに触発され、次の2)と3)を許可するように変更された双方向のクラスです。
ご了承ください :
bd.inverse
自動的に更新bd
されます。bd.inverse[value]
は、常に次のようなリストです。key
bd[key] == value
bidict
モジュールとは異なり、ここでは同じ値を持つ2つのキーを持つことができます。これは非常に重要です。コード:
class bidict(dict):
def __init__(self, *args, **kwargs):
super(bidict, self).__init__(*args, **kwargs)
self.inverse = {}
for key, value in self.items():
self.inverse.setdefault(value,[]).append(key)
def __setitem__(self, key, value):
if key in self:
self.inverse[self[key]].remove(key)
super(bidict, self).__setitem__(key, value)
self.inverse.setdefault(value,[]).append(key)
def __delitem__(self, key):
self.inverse.setdefault(self[key],[]).remove(key)
if self[key] in self.inverse and not self.inverse[self[key]]:
del self.inverse[self[key]]
super(bidict, self).__delitem__(key)
使用例:
bd = bidict({'a': 1, 'b': 2})
print(bd) # {'a': 1, 'b': 2}
print(bd.inverse) # {1: ['a'], 2: ['b']}
bd['c'] = 1 # Now two keys have the same value (= 1)
print(bd) # {'a': 1, 'c': 1, 'b': 2}
print(bd.inverse) # {1: ['a', 'c'], 2: ['b']}
del bd['c']
print(bd) # {'a': 1, 'b': 2}
print(bd.inverse) # {1: ['a'], 2: ['b']}
del bd['a']
print(bd) # {'b': 2}
print(bd.inverse) # {2: ['b']}
bd['b'] = 3
print(bd) # {'b': 3}
print(bd.inverse) # {2: [], 3: ['b']}
self[key]
し__delitem__()
て、の繰り返しルックアップを最適化することvalue = self[key]
です。しかし...ええ。それはごくわずかです。純粋に素晴らしい、Basjをありがとう!
キーと値のペアを逆の順序で追加することで、同じdict自体を使用できます。
d = {'a':1、 'b':2} revd = dict([d.items()]のiのreversed(i)) d.update(revd)
d.update( dict((d[k], k) for k in d) )
。
dict((v, k) for (k, v) in d.items())
。いずれの場合も、ペアを直接.update:に渡すことができますd.update(reversed(i) for i in d.items())
。
d={'a':1, 'b':2, 1: 'b'}
dict(map(reversed, a_dict.items()))
。
d.update(revd)
を無視するのは素晴らしいので、私はまだ賛成票を検討しています。これについて考えてみましょう。
貧乏人の双方向ハッシュテーブルは、2つの辞書だけを使用することです(これらはすでに高度に調整されたデータ構造です)。
インデックスにはbidictパッケージもあります。
bidictのソースはgithubにあります:
以下のコードスニペットは、可逆(全単射)マップを実装しています。
class BijectionError(Exception):
"""Must set a unique value in a BijectiveMap."""
def __init__(self, value):
self.value = value
msg = 'The value "{}" is already in the mapping.'
super().__init__(msg.format(value))
class BijectiveMap(dict):
"""Invertible map."""
def __init__(self, inverse=None):
if inverse is None:
inverse = self.__class__(inverse=self)
self.inverse = inverse
def __setitem__(self, key, value):
if value in self.inverse:
raise BijectionError(value)
self.inverse._set_item(value, key)
self._set_item(key, value)
def __delitem__(self, key):
self.inverse._del_item(self[key])
self._del_item(key)
def _del_item(self, key):
super().__delitem__(key)
def _set_item(self, key, value):
super().__setitem__(key, value)
この実装の利点はinverse
、aの属性BijectiveMap
が再びaになることBijectiveMap
です。したがって、次のようなことができます。
>>> foo = BijectiveMap()
>>> foo['steve'] = 42
>>> foo.inverse
{42: 'steve'}
>>> foo.inverse.inverse
{'steve': 42}
>>> foo.inverse.inverse is foo
True
残念ながら、最高評価の回答はbidict
機能しません。
3つのオプションがあります。
サブクラスdict:のサブクラスを作成できますdict
が、注意してください。あなたはのカスタム実装を記述する必要がありますupdate
、pop
、initializer
、setdefault
。dict
実装は呼び出すことはありません__setitem__
。これが、最高評価の回答に問題がある理由です。
UserDictから継承:すべてのルーチンが正しく呼び出されることを除けば、これはdictと同じです。と呼ばれるアイテムで、内部でdictを使用しますdata
。あなたは読むことができますPythonのドキュメントを、あるいはPythonの3で動作することにより、指向リストの簡単な実装を使用します。逐語的に含めなかったことをお詫びします:私はその著作権がわかりません。
抽象基本クラスから継承:collections.abcから継承すると、新しいクラスのすべての正しいプロトコルと実装を取得するのに役立ちます。これは、暗号化してデータベースにキャッシュすることもできない限り、双方向辞書にとってはやり過ぎです。
TL; DR-これをコードに使用します。詳細については、TreyHunnerの記事をお読み ください。
このようなもの、多分:
import itertools
class BidirDict(dict):
def __init__(self, iterable=(), **kwargs):
self.update(iterable, **kwargs)
def update(self, iterable=(), **kwargs):
if hasattr(iterable, 'iteritems'):
iterable = iterable.iteritems()
for (key, value) in itertools.chain(iterable, kwargs.iteritems()):
self[key] = value
def __setitem__(self, key, value):
if key in self:
del self[key]
if value in self:
del self[value]
dict.__setitem__(self, key, value)
dict.__setitem__(self, value, key)
def __delitem__(self, key):
value = self[key]
dict.__delitem__(self, key)
dict.__delitem__(self, value)
def __repr__(self):
return '%s(%s)' % (type(self).__name__, dict.__repr__(self))
複数のキーに特定の値がある場合、何を実行するかを決定する必要があります。特定のペアの双方向性は、後で挿入したペアによって簡単に破壊される可能性があります。私は1つの可能な選択肢を実装しました。
例:
bd = BidirDict({'a': 'myvalue1', 'b': 'myvalue2', 'c': 'myvalue2'})
print bd['myvalue1'] # a
print bd['myvalue2'] # b
dict([('a', 'b'), ('b', 'c')]); dict['b']
-> 。'c'
'a'
print bd['myvalue2']
答えb, c
(または[b, c]
、または(b, c)
、または他の何か)?
まず、値のマッピングへのキーが1対1であることを確認する必要があります。そうしないと、双方向マップを作成できません。
次に、データセットの大きさはどれくらいですか?データが少ない場合は、2つの別々のマップを使用し、更新時に両方を更新します。または、更新/削除が組み込まれた、2つのdictの単なるラッパーであるBidictのような既存のソリューションを使用することをお勧めします。
ただし、データセットが大きく、2つのdictを維持することが望ましくない場合:
キーと値の両方が数値の場合は、補間を使用してマッピングを概算する可能性を検討してください。キーと値のペアの大部分をマッピング関数(およびその
逆関数)でカバーできる場合は、外れ値をマップに記録するだけで済みます。
ほとんどのアクセスが単方向(キー->値)の場合、時間を
スペースと交換するために、リバースマップを段階的に作成することはまったく問題ありません。
コード:
d = {1: "one", 2: "two" }
reverse = {}
def get_key_by_value(v):
if v not in reverse:
for _k, _v in d.items():
if _v == v:
reverse[_v] = _k
break
return reverse[v]