Pythonで別のクラス内にクラスを定義する利点はありますか?


106

ここで私が話しているのは、ネストされたクラスです。基本的に、私はモデリングしている2つのクラスがあります。DownloadManagerクラスとDownloadThreadクラス。ここでの明白なOOPの概念は構成です。でも、作曲は必ずしも入れ子という意味ではないですよね?

次のようなコードがあります。

class DownloadThread:
    def foo(self):
        pass

class DownloadManager():
    def __init__(self):
        dwld_threads = []
    def create_new_thread():
        dwld_threads.append(DownloadThread())

しかし、今は入れ子の方が良い状況があるかどうか疑問に思っています。何かのようなもの:

class DownloadManager():
    class DownloadThread:
        def foo(self):
            pass
    def __init__(self):
        dwld_threads = []
    def create_new_thread():
        dwld_threads.append(DownloadManager.DownloadThread())

回答:


121

これは、「内部」クラスが1回限りのもので、外部クラスの定義の外では決して使用されない場合に行うことができます。たとえば、メタクラスを使用するために、時には便利です

class Foo(object):
    class __metaclass__(type):
        .... 

メタクラスを個別に定義するのではなく、一度しか使用しない場合。

ネストされたクラスをこのように使用したのは他に1回だけですが、外部クラスを名前空間としてのみ使用して、密接に関連する一連のクラスをグループ化しました。

class Group(object):
    class cls1(object):
       ...

    class cls2(object):
       ...

次に、別のモジュールから、Groupをインポートして、Group.cls1、Group.cls2などとして参照できます。ただし、モジュールを使用することで、まったく同じように(混乱の少ない方法で)達成できると主張する人もいます。


13
2番目のケースでは、名前空間になるように設計されたモジュールまたはパッケージを使用するほうが確実にサービスが提供されると私は主張します。
マシュートレバー

5
これは素晴らしい答えです。簡単に言えば、モジュールを使用する別の方法に言及しています。+1
CornSmith 2013年

5
ちなみに、頑固にコードを階層パッケージと適切なモジュールに整理できない、または整理できない人は、クラスを名前空間として使用する方が、何も使用しないよりも良い場合があります。
Acumenus 14

19

私はPythonを知りませんが、あなたの質問は非常に一般的です。Pythonに固有の場合は無視してください。

クラスの入れ子はスコープのすべてです。あるクラスが別のクラスのコンテキストでのみ意味があると考える場合、前者はおそらくネストされたクラスになるのに適した候補です。

これは、プライベートなネストされたクラスとしての一般的なパターンのmakeヘルパークラスです。


9
参考までに、privateクラスの概念はPythonではあまり適用されませんが、暗黙の使用範囲は依然として確実に適用されます。
Acumenus 2014

7

ネストされたクラスには、拡張機能が特定のネストされたクラスにカプセル化された継承クラスを構築したい場合の別の使用法があります。

この例を見てください:

class foo:

  class bar:
    ...  # functionalities of a specific sub-feature of foo

  def __init__(self):
    self.a = self.bar()
    ...

  ...  # other features of foo


class foo2(foo):

  class bar(foo.bar):
    ... # enhanced functionalities for this specific feature

  def __init__(self):
    foo.__init__(self)

コンストラクタにそのノートfoo、線がself.a = self.bar()建設するfoo.barオブジェクトが構築されているときには、実際にはfoo、オブジェクト、およびfoo2.barオブジェクトが構築されているオブジェクトは実際にfoo2オブジェクト。

クラスが場合はbar、クラスの外で定義されたfoo代わりに、だけでなく、(と呼ばれることになるその継承されたバージョンbar2たとえば)を、その後、新しいクラスを定義するfoo2のconstuctorがあるため、はるかに痛いだろうfoo2に置き換え、その最初の行を持っている必要がありself.a = bar2()、これは、コンストラクタ全体を書き直すことを意味します。


6

メタクラスを扱っている場合を除いて、実際にこれを行うメリットはありません。

クラス:スイートは実際にはあなたが思っているものではありません。それは奇妙なスコープであり、奇妙なことをします。それは本当にクラスを作ることさえしません!これは、いくつかの変数(クラスの名前、ベース、属性の小さな辞書、メタクラス)を収集する方法にすぎません。

名前、ディクショナリ、ベースはすべてメタクラスである関数に渡され、それから、class:suiteがあったスコープの変数 'name'に割り当てられます。

メタクラスをいじって、実際にストック標準クラス内にクラスをネストすることで得られるものは、コードを読みにくく、コードを理解することが難しく、奇妙なエラーであり、「クラス」の理由に精通していなければ理解するのが非常に困難です。スコープは他のpythonスコープとは完全に異なります。


3
同意しない:クラスのユーザーは面倒なインポートを行わなくてもカスタム例外を確認できるため、例外クラスのネストは非常に便利です。例except myInstanceObj.CustomError, e:
RobM 2010年

2
@Jerub、なぜそんなに悪いの?
Robert Siemer

5

クラスをクラスジェネレーターとして使用している可能性があります。のように(いくつかのカフコードから:)

class gen(object):
    class base_1(object): pass
    ...
    class base_n(object): pass

    def __init__(self, ...):
        ...
    def mk_cls(self, ..., type):
        '''makes a class based on the type passed in, the current state of
           the class, and the other inputs to the method'''

この機能が必要なときに、それが非常に明確になると思います。同様のことをする必要がない場合は、おそらく良いユースケースではありません。


1

いいえ、構成はネストを意味しません。外部クラスの名前空間でさらに非表示にしたい場合は、クラスをネストすることは理にかなっています。

とにかく、私はあなたのケースで入れ子にするための実用的な使用を見ません。コードを読みにくく(理解し)、インデントを増やして行を短くし、分割しやすくします。

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