dictのように機能するpythonクラス


113

のように動作するカスタムクラスを記述したいdictので、から継承していdictます。

しかし、私の質問は、dict__init__()メソッドでプライベートメンバーを作成する必要がありますか?」です。dict単純にから継承した場合はすでに動作しているため、この点はわかりませんdict

ほとんどの継承スニペットが以下のように見える理由を誰かが指摘できますか?

class CustomDictOne(dict):
   def __init__(self):
      self._mydict = {} 

   # other methods follow

単純な代わりに...

class CustomDictTwo(dict):
   def __init__(self):
      # initialize my other stuff here ...

   # other methods follow

実際、この質問に対する答えは、ユーザーがあなたの辞書に直接アクセスできないようにすることだと思います(つまり、ユーザーはあなたが提供したアクセス方法を使用する必要があります)。

しかし、配列アクセス演算子は[]どうですか?それをどのように実装しますか?これまでのところ、[]演算子をオーバーライドする方法を示す例は見ていません。

その場合は[]、アクセス機能は、カスタムクラスで提供されていない、継承された基本法は、異なる辞書上で動作するのでしょうか?

次のスニペットを試して、Python継承の理解をテストしました。

class myDict(dict):
    def __init__(self):
        self._dict = {}

    def add(self, id, val):
        self._dict[id] = val


md = myDict()
md.add('id', 123)
print md[id]

次のエラーが発生しました:

KeyError:<組み込み関数ID>

上記のコードの何が問題になっていますか?

myDictこのようなコードを記述できるようにクラスを修正するにはどうすればよいですか?

md = myDict()
md['id'] = 123

[編集]

上記のコードサンプルを編集して、デスクから離れる前に行った愚かなエラーを取り除きました。これはタイプミスでした(エラーメッセージから見つけたはずです)。

回答:



104
class Mapping(dict):

    def __setitem__(self, key, item):
        self.__dict__[key] = item

    def __getitem__(self, key):
        return self.__dict__[key]

    def __repr__(self):
        return repr(self.__dict__)

    def __len__(self):
        return len(self.__dict__)

    def __delitem__(self, key):
        del self.__dict__[key]

    def clear(self):
        return self.__dict__.clear()

    def copy(self):
        return self.__dict__.copy()

    def has_key(self, k):
        return k in self.__dict__

    def update(self, *args, **kwargs):
        return self.__dict__.update(*args, **kwargs)

    def keys(self):
        return self.__dict__.keys()

    def values(self):
        return self.__dict__.values()

    def items(self):
        return self.__dict__.items()

    def pop(self, *args):
        return self.__dict__.pop(*args)

    def __cmp__(self, dict_):
        return self.__cmp__(self.__dict__, dict_)

    def __contains__(self, item):
        return item in self.__dict__

    def __iter__(self):
        return iter(self.__dict__)

    def __unicode__(self):
        return unicode(repr(self.__dict__))


o = Mapping()
o.foo = "bar"
o['lumberjack'] = 'foo'
o.update({'a': 'b'}, c=44)
print 'lumberjack' in o
print o

In [187]: run mapping.py
True
{'a': 'b', 'lumberjack': 'foo', 'foo': 'bar', 'c': 44}

37
サブクラスdict化する場合superは、単にインスタンスに委譲するのではなく、オブジェクト自体を(を使用して)使用__dict__する必要があります。これは、基本的に、インスタンスごとに2つのディクテーションを作成することを意味します。
アーロンホール

8
自己.__ dict__にはありません、実際の辞書コンテンツと同じ。すべてのpythonオブジェクトは、そのタイプに関係なく、_dict__すべてのオブジェクト属性(メソッド、フィールドなど)を含むを持っています。自分自身を変更するコードを書きたいのでなければ、これをいじりたくありません...
Raik

85

このような

class CustomDictOne(dict):
   def __init__(self,*arg,**kw):
      super(CustomDictOne, self).__init__(*arg, **kw)

今、あなたは、組み込み関数を使用するようにすることができますdict.get()ようにself.get()

非表示をラップする必要はありませんself._dict。あなたのクラスがすでにあるのdict。


3
この。dict最初にコンストラクターを呼び出さずに継承する意味はありません。
sykora

1
継承dictには実際には2つのdict-instanceが含まれていることに注意してください。1つ目は継承されたコンテナ、2つ目はクラス属性を保持するdict です。スロットを使用することで回避できます
ankostis

1
__dict__それが最初にアクセスしたときに、実際にはそれほど限り、ユーザーはそれを使用しようとしていないとして、それは大丈夫です、作成されます。__slots__でもいいだろう。
アーロンホール

9
あなたが残したカンマの後にスペースを使用してください!;-)
James Burke、

10

完全を期すために、最新のPython 2.x(執筆時点では2.7.7)について@björn-pollexが言及したドキュメントへのリンクを次に示します。

コンテナタイプのエミュレート

(コメント機能を使用していないため申し訳ありませんが、stackoverflowで使用することは許可されていません。)


3

このコードのチャンクの問題:

class myDict(dict):
    def __init__(self):
        self._dict = {}

    def add(id, val):
        self._dict[id] = val


md = myDict()
md.add('id', 123)

...次のように、「add」メソッド(...およびクラスのメンバーになりたいメソッド)は、最初の引数として明示的な「self」を宣言する必要があります。

def add(self, 'id', 23):

演算子のオーバーロードを実装してキーで項目にアクセスするには、ドキュメントでマジックメソッド__getitem____setitem__

PythonはDuck Typingを使用するため、言語のdictクラスからカスタムdictクラスを派生する理由は実際にはないことに注意してください-何をしようとしているのか(たとえば、このインスタンスを渡す必要がある場合)を除きisinstance(MyDict(), dict) == True、どこかで壊れるコードにクラスを分類します)、クラスを十分にdictのようなものにするAPIを実装してそこで停止した方がよい場合があります。


3

ここに代替ソリューションがあります:

class AttrDict(dict):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.__dict__ = self

a = AttrDict()
a.a = 1
a.b = 2

カスタムメソッドを定義しておらず、他の答えが述べているように他の問題もあるので、これは悪いことです。
Shital Shah

これはまさに私が必要としたものです。ありがとうございました!
jakebrinkmann

2

これに対する正しい答えは本当にどこにもありません

class MyClass(dict):
    
    def __init__(self, a_property):
        self[a_property] = a_property

あなたが本当にしなければならないのはあなた自身のものを定義することだけです__init__-それは本当にそれだけでもあります。

別の例(もう少し複雑):

class MyClass(dict):

    def __init__(self, planet):
        self[planet] = planet
        info = self.do_something_that_returns_a_dict()
        if info:
            for k, v in info.items():
                self[k] = v

    def do_something_that_returns_a_dict(self):
        return {"mercury": "venus", "mars": "jupiter"}

この最後の例は、なんらかのロジックを埋め込む場合に便利です。

とにかく...要するにclass GiveYourClassAName(dict)、クラスを口述のように振る舞わせるのに十分です。どんなdict操作でもself、通常のdictと同じです。


1

これが私の最善の解決策です。これを何度も使った。

class DictLikeClass:
    ...
    def __getitem__(self, key):
        return getattr(self, key)

    def __setitem__(self, key, value):
        setattr(self, key, value)
    ...

次のように使用できます。

>>> d = DictLikeClass()
>>> d["key"] = "value"
>>> print(d["key"])

0

Python組み込みのdictを継承しないでください!たとえば、updateメソッドはを使用__setitem__しません。最適化のために多くのことを行います。UserDictを使用します。

from collections import UserDict

class MyDict(UserDict):
    def __delitem__(self, key):
        pass
    def __setitem__(self, key, value):
        pass

1
誰かが組み込みのdictから継承してはいけないものに基づいて?ドキュメント(docs.python.org/3/library/collections.html#collections.UserDict)から:「このクラスの必要性は、dictから直接サブクラス化する機能によって部分的に取って代わられましたが、このクラスは基礎となるディクショナリは属性としてアクセスできるため、で動作します。」また、同じページ:コレクションモジュールは「バージョン3.3で非推奨になり、バージョン3.9で削除されます:コレクションの抽象基本クラスをcollections.abcモジュールに移動しました。」...これにはUserDictがありません。
NumesSanguis

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.