2

ここでPython初心者からのスタイルの無能さを許してください。

初期データを確立するための1つのパラメーターを取るクラスがあります。初期データを取得する方法は2つあります。文字列のリスト、または文字列キーと整数値を含む辞書です。

現在、私はコンストラクターの1つのバージョン、つまりパラメーターのディクショナリを取得するバージョンのみを実装しています。デフォルト値は{}です。リストパラメータのinitは、メソッドとして実装されます。

myClass = MyClass()
myClass.initList(listVar)

私は確かにこれと一緒に暮らすことができますが、これは確かに完璧ではありません。それで、私はいくつかのPythonicの知恵のためにここに目を向けることに決めました:そのような多形コンストラクターはどのように実装されるべきですか?これが辞書であるかどうかをスニッフィングするために、initData.keys()を読み取ろうとして失敗する必要がありますか?あるいは、パラメータタイプをスニッフィングして、設計上歓迎されないようなお粗末なポリモーフィズムを実装することは、非pythonicと見なされますか?

4

4 に答える 4

3

理想的な世界では、違いを知らない(つまり、ダックタイピング)listかどうかを判断できるコンストラクターを1つ作成します。dictもちろん、これらはかなり異なるアヒルであるため、これはあまり現実的ではありません。

当然のことながら、実際のインスタンスタイプをチェックするというアイデアは、ダックタイピングのアイデアとは異なるため、少し胸焼けがあります。abcしかし、Python 2.6では、「抽象基本クラス」の定義を可能にするという興味深いモジュールが導入されました。抽象基本クラスのインスタンスと見なされるには、実際にそれを継承する必要はなく、すべての抽象メソッドを実装するだけで済みます。

このcollectionsモジュールには、ここで関心のあるいくつかの抽象基本クラス、つまりcollections.Sequenceとが含まれていcollections.Mappingます。__init__したがって、次のような関数を記述できます。

def __init__(self, somedata):
    if isinstance(somedata, collections.Sequence):
        # somedata is a list or some other Sequence
    elif isinstance(somedata, collections.Mapping):
        # somedata is a dict or some other Mapping

http://docs.python.org/2/library/collections.html#collections-abstract-base-classesには、各ABCによって提供されるメソッドの詳細が含まれています。これらに固執すると、コードはこれらの抽象基本クラスの1つに適合する任意のオブジェクトを受け入れることができるようになります。dictそして、ビルトインとタイプを使用する限りlist、次のことがわかります。

>>> isinstance([], collections.Sequence)
True
>>> isinstance([], collections.Mapping)
False
>>> isinstance({}, collections.Sequence)
False
>>> isinstance({}, collections.Mapping)
True

そして、ほとんど偶然に、あなたはそれをうまく機能させましたtuple。あなたはおそらくそれが本当にであるかどうか気にしないでしょうlist、ただあなたがそれから要素を読むことができるということだけです。ただし、チェックisinstance(somedata, list)した場合は除外されtupleます。これは、ABCを使用することで購入できるものです。

于 2013-03-15T15:40:14.737 に答える
2

@ Jan-PhilipGehrckeが指摘しているpythonicように、定量化するのは難しい場合があります。私にとってそれは意味します:

  • 読みやすい
  • メンテナンスが簡単
  • 単純な方が複雑な方が良い
  • etcetera、etceteraなど(インタプリタZen of Pythonに入力して取得する完全なリストについては、を参照してください)import this

したがって、最もpythonicなソリューションは、サポートされている初期化子ごとに何をする必要があるか、およびそれらの数に依存します。ほんの一握りで、それぞれが数行のコードで処理できる場合は、andを使用isinstance__init__ます。

class MyClass(object):

    def __init__(self, initializer):
        """
        initialize internal data structures with 'initializer'
        """
        if isinstance(initializer, dict):
            for k, v in itit_dict.items():
                # do something with k & v
                setattr(self, k, v)
        elif isinstance(initializer, (list, tuple)):
            for item in initializer:
                setattr(self, item, None)

一方、可能な初期化子が多数ある場合、またはそれらのいずれかで処理するために多くのコードが必要な場合は、classmethod可能なinitタイプごとに1つのコンストラクターが必要になります。最も一般的な使用法は、次の__init__とおりです。

class MyClass(object):

    def __init__(self, init_dict={}):
        """
        initialize internal data structures with 'init_dict'
        """
        for k, v in itit_dict.items():
            # do something with k & v
            setattr(self, k, v)

    @classmethod
    def from_sequence(cls, init_list):
        """
        initialize internal  data structures with 'init_list'
        """
        result = cls()
        for item in init_list:
            setattr(result, item, None)
        return result

これにより、可能な各コンストラクターがシンプルでクリーン、そして理解しやすくなります。

補足として:デフォルトとして可変オブジェクトを使用することは(上記のように__init__)注意して行う必要があります。その理由は、デフォルトは1回だけ評価され、その後のすべての呼び出しで結果が何であれ使用されるためです。これは、関数でそのオブジェクトを変更する場合にのみ問題になります。これらの変更は、後続のすべての呼び出しで確認されるためです。また、探している動作ではない可能性のあるキャッシュを作成する場合を除きます。私は変更していないので、これは私の例では問題ではありません。init_dictそれを繰り返すだけです(これは、呼び出し元が空であるために置き換えていない場合は何もしません)。

于 2013-03-15T14:33:19.393 に答える
2

Pythonには関数のオーバーロードはありません。メソッドでの使用if isinstance(...)は、__init__()非常に読みやすく、理解しやすいでしょう。

class Foo(object):
    def __init__(self, arg):
        if isinstance(arg, dict):
            ...
        elif isinstance(arg, list):
            ...
        else:
            raise Exception("big bang")
于 2013-03-15T14:02:43.867 に答える
-1

あなたはそれを使うことができ*argsます**kwargs。ただし、どのタイプのパラメーターを使用するかを知りたい場合は、type()またはを使用する必要がありますisinstance()

于 2013-03-15T14:04:27.677 に答える