32

これは、理論的な部分と実際的な部分の2つの質問です。

dictをサブクラス化する場合:

class ImageDB(dict):
    def __init__(self, directory):
        dict.__init__(self)  # Necessary?? 
        ...

dict.__init__(self)「安全」対策と同じように(たとえば、重要な実装の詳細が重要な場合に)呼び出す必要がありますか?呼び出されdict.__init__()ない場合、Pythonの将来のバージョンでコードが破損するリスクはありますか?私はここで、どちらか一方を行う根本的な理由を探しています(実際には、電話dict.__init__()は安全です)。

私の推測では、ImageDB.__init__(self, directory)が呼び出されたとき、selfはすでに新しい空のdictオブジェクトであり、したがって呼び出す必要はありませんdict.__init__(最初はdictを空にしたいです)。これは正しいです?

編集

上記の基本的な質問の背後にあるより実用的な質問は次のとおりです。私は(常にdb.contents […]を実行する代わりに)db […]構文を頻繁に使用するため、dictをサブクラス化することを考えていました。オブジェクトの唯一のデータ(属性)は、実際には実際にはdictです。get_image_by_name()データベースにいくつかのメソッド(たとえば、、、など)を追加し、get_image_by_code()をオーバーライドするだけにします__init__()。これは、イメージデータベースがそれを含むディレクトリによって定義されているためです。

要約すると、(実用的な)質問は次のようになります。初期化が異なり(ディレクトリ名のみを使用する)、追加のメソッドがあることを除いて、辞書のように動作するものの適切な実装は何ですか?

「工場」は多くの回答で言及されました。つまり、すべてを要約すると、dictをサブクラス化し、メソッドをオーバーライド__init__()して追加しますか、それとも、メソッドを追加するdictを返す(ファクトリ)関数を記述しますか?ファクトリ関数は、追加のセマンティクスとメソッドがあることをタイプが示さないオブジェクトを返すため、最初のソリューションを好む傾向がありますが、どう思いますか?

編集2

新しいクラスが「辞書ではない」場合、特にその__init__メソッドがdictと同じ引数を取ることができない__init__場合(「実用的な質問」の場合)、 dictをサブクラス化することはお勧めできません。その上)。言い換えれば、私が正しく理解していれば、コンセンサスは次のように思われます。サブクラス化する場合、すべてのメソッド(初期化を含む)は基本クラスのメソッドと同じシグネチャを持っている必要があります。これにより、isinstance(subclass_instance、dict)は、たとえば、のsubclass_instance.__init__()ように使用できることを保証できます。dict.__init__()

次に、別の実用的な質問が表示されます。初期化メソッドを除いて、dictと同じようなクラスをどのように実装する必要がありますか?サブクラス化せずに?これには、面倒なボイラープレートコードが必要になりますね。

4

5 に答える 5

17

dict.__init__(self)サブクラス化するときは、おそらく呼び出す必要があります。実際、dict で何が起こっているのか正確にはわかりません (これは組み込みであるため)。これは、バージョンや実装によって異なる可能性があります。dict が内部データ構造を保持している場所がわからないため、呼び出さないと不適切な動作が発生する可能性があります。

ところで、あなたは何をしたいのか教えてくれませんでした。dict (マッピング) 動作を備えたクラスが必要であり、実際には dict が必要ない場合 (たとえばisinstance(x, dict)、ソフトウェアのどこにもコードを実行する必要がない場合)、おそらく使用する方が良いでしょUserDict.UserDictUserDict.DictMixin。 python <= 2.5 を使用collections.MutableMappingしている場合、または python >= 2.6 を使用している場合。これらは、クラスに優れたdict動作を提供します。

編集:別のコメントで、dictのメソッドをオーバーライドしていないことを読みました! その場合、サブクラス化する意味はまったくありません。そうしないでください。

def createImageDb(directory):
    d = {}
    # do something to fill in the dict
    return d

EDIT 2: dict から継承して新しいメソッドを追加したいが、オーバーライドする必要はありません。より良い選択は次のとおりです。

class MyContainer(dict):
    def newmethod1(self, args):
        pass

    def newmethod2(self, args2):
        pass


def createImageDb(directory):
    d = MyContainer()
    # fill the container
    return d

ところで、どのメソッドを追加していますか? 適切な抽象化を作成していますか? おそらく、必要なメソッドを定義するクラスを使用し、内部で「通常の」辞書を使用する方がよいでしょう。

工場関数: http://en.wikipedia.org/wiki/Factory_method_pattern

これは、コンストラクターをオーバーライド/変更するのではなく、インスタンスの構築を関数に委任する方法にすぎません。

于 2010-01-09T12:55:56.643 に答える
12

通常は基本クラスを呼び出す必要がありますが__init__、なぜここで例外を作成するのですか?

オーバーライドしないか、 呼び出し基本クラス__init__をオーバーライドする必要がある場合、引数が心配な場合は* args、** kwargs、または空のdictが必要な場合は何も渡さないでください。__init____init__

class MyDict(dict):
    def __init__(self, *args, **kwargs ):
        myparam = kwargs.pop('myparam', '')
        dict.__init__(self, *args, **kwargs )

基本クラスが何をしているか、何をしていないかを想定するべきではありません。基本クラスを呼び出さないのは間違っています。__init__

于 2010-01-09T13:56:43.183 に答える
3

dict; をサブクラス化するときは酸洗に注意してください。たとえば、これには 2.7 では __getnewargs__ が必要で、古いバージョンでは __getstate__ __setstate__ が必要かもしれません。(理由はわかりません。)

class Dotdict( dict ):
    """ d.key == d["key"] """

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

    def __getnewargs__(self):  # for cPickle.dump( d, file, protocol=-1)
        return tuple(self)
于 2010-01-27T15:51:22.363 に答える
2

PEP 372は、順序付けられた dict を collections モジュールに追加することを扱います。

「dict のサブクラス化は重要な作業であり、多くの実装ではすべてのメソッドが適切にオーバーライドされていないため、予期しない結果が生じる可能性がある」と警告しています。

python3.1 への提案された (そして受け入れられた)パッチ__init__は、次のような を使用します。

+class OrderedDict(dict, MutableMapping):
+    def __init__(self, *args, **kwds):
+        if len(args) > 1:
+            raise TypeError('expected at most 1 arguments, got %d' % len(args))
+        if not hasattr(self, '_keys'):
+            self._keys = []
+        self.update(*args, **kwds)

これに基づいて、dict.__init__()呼び出す必要はないようです。

編集:のメソッドをオーバーライドまたは拡張していない場合はdict、Alan Franzoni に同意します。サブクラス化ではなく dict ファクトリを使用します。

def makeImageDB(*args,**kwargs):
   d = {}
   # modify d
   return d
于 2010-01-09T12:06:19.840 に答える