18

この質問は、以前に質問された this onethis one に基づいていますが、より微妙な点、具体的には「内部クラス」とは何ですか?

これが私の状況です。クラスの多くが外部で構築されることを意図していない MS Office ファイルを操作するための Python ライブラリを構築していますが、それらのメソッドの多くは API の重要な部分です。例えば:

class _Slide(object):
    def add_shape(self):
        ...

_Slideライブラリのエンドユーザーとして、 を呼び出して新しいスライドを取得しますPresentation.add_slide()。ただし、スライドを作成したら、必ずadd_shape(). したがって、メソッドは API の一部ですが、コンストラクターはそうではありません。プレゼンテーション クラスだけが外部/API コンストラクターを持つため、この状況はライブラリーで何十回も発生します。

PEP8 はこの点であいまいであり、何が内部クラスと見なされるかを詳しく説明せずに「内部クラス」に言及しています。この場合、クラスは「部分的に内部」であると言えると思います。

問題は、少なくとも 3 つの異なる状況を区別するための表記が 2 つしかないことです。

  1. クラスのコンストラクターと「public/API」メソッドはすべて使用可能です。
  2. コンストラクターは使用されませんが、クラスの「public/API」メソッドは使用されます。
  3. このクラスは実際には内部的なものであり、パブリック API はありません。将来のリリースで変更または削除する権利を留保します。

コミュニケーションの観点から、外部 API (ライブラリーのエンドユーザー対象) と内部 API (開発者対象) の明確な表現の間には矛盾があることに注意してください。エンド ユーザーにとって、通話_Slide()は禁止/自己責任です。_random_helper_method()ただし、これは内部 API のハッピー メンバーであり、内部からのみ呼び出されるべきものとは異なります_Slide()

この質問について、私に役立つ可能性のある視点はありますか? エンドユーザー API の明確さのために戦うためにクラス名に「一重アンダースコア」の弾薬を使用することが慣例で定められているのでしょうか、それとも、クラス API が実際にいつ使用されるかについて自分自身や他の開発者に伝えるためにそれを予約してもよろしいでしょうか?たとえば、変更される可能性のある実装の詳細であるため、それが存在するモジュールの外部のオブジェクトによって使用されませんか?


更新:数年間のさらなる熟考の後、モジュール (ファイル) の外部のクラスとしてアクセスすることを意図していないクラスに名前を付けるときに、先頭にアンダースコアを使用するという規則に落ち着きました。このようなアクセスは通常、そのクラスのオブジェクトをインスタンス化するため、またはクラス メソッドにアクセスするためなどです。

これは、モジュールのユーザー (多くの場合、もちろん自分自身です:) に基本的な指標を提供します。(単体テスト モジュールはこの規則の例外です。このようなインポートは、「内部」クラスの単体テストによく現れる場合があります。)

これはclassへのアクセスであることに注意してください。おそらくファクトリなどによって提供される、モジュールの外部のそのクラス (型)のオブジェクトへのアクセスは完全に問題なく、おそらく期待されています。クラスから作成されたオブジェクトとクラスを区別できなかったことが、最初の混乱の原因だったと思います。

from module import *この規則には、ステートメントを作成するときにこれらのクラスを含めないという副次的な利点もあります。私自身のコードでこれらを使用することはなく、避けることをお勧めしますが、これらのクラス識別子はモジュール インターフェイスの一部になることを意図していないため、これは適切な動作です。

これは何年にもわたる試行錯誤の末の私の個人的な「ベスト プラクティス」であり、もちろん「正しい」方法ではありません。あなたのマイレージは異なる場合があります。

4

4 に答える 4

4

質問の説明だけではわかりませんが、コメントで提供された追加情報から、あなたのSlideクラスは実際には公開されていると思います。

add_slide()これは、インスタンスがa のメソッドを呼び出すことによってのみ間接的に作成されるという事実にもかかわらず、当てはまります。これはPresentation、呼び出し元がインスタンスのメソッドを後で操作するために自由に呼び出せる (そして必要になる可能性が高い) ためです。私の意見では、真にプライベートなクラスは、それを「所有」するクラスのメソッドによってのみアクセスされます。

他の方法で物事を処理すると、カプセル化が壊れ、設計のコンポーネント間の結合が増加します。どちらも望ましくなく、柔軟性と再利用性のためにできる限り避ける必要があります。

于 2013-08-31T21:28:22.780 に答える
4

マルティノーの答えは良いものだと思います。確かに最も単純で、間違いなく最もpythonicです。

ただし、これが唯一の選択肢ではありません。

よく使用される手法は、パブリック メソッドをインターフェイス型の一部として定義することです。たとえばzope.interface.Interface、ねじれたフレームワークで広く使用されています。現代の python はおそらくabc.ABCMeta同じ効果のために使用します。基本的に、パブリック メソッドは、より多くのパブリック メソッドを持つPresentation.add_slideのインスタンスを返すものとして文書化されています。AbstractSlideしかし、 のインスタンスをAbstractSlide直接構築することはできないため、インスタンスを作成するパブリックな方法はありません。

public メソッドのみが呼び出されることをアサートできる抽象型のモック インスタンスを作成できるため、この手法は特に便利です。単体テストに非常に役立ちます (特にライブラリのユーザーにとっては、パブリック インターフェイスのみを使用していることを確認できます)。

別のオプション; スライド クラスのインスタンスを作成する公開されている唯一の方法はPresentation.add_slide;を使用することです。それを文字通りコンストラクタにすることができます。これには、おそらくいくつかのメタクラスの悪ふざけが必要になるでしょう。

from functools import partial

class InstanceConstructor(type):
    def __get__(cls, instance, owner):
        if instance is not None:
            return partial(cls, instance)
        return cls

クラスadd_slideの で動作を定義するだけです。__init__

>>> class Presentation(object):
...     def __init__(self):
...         self.slides = []
...
...     class add_slide(object):
...         __metaclass__ = InstanceConstructor
...
...         def __init__(slide, presentation, color):
...             slide.color = color
...             presentation.slides.append(slide)
...
>>> p = Presentation()
>>> p.add_slide('red')
<instance_ctor.add_slide object at ...>
>>> p.slides
[<instance_ctor.add_slide object at ...>]
>>> p.slides[0].color
'red'
于 2013-08-31T21:55:26.643 に答える
1

「内部」クラスとは慣習の問題です。これが、PEP が用語を定義しなかった理由ですが、これらをユーザーに公開している場合 (このクラスを返すメソッドを提供するか、このクラスが渡されることに依存している場合)周り)それは実際には「内部」ではありません。

選択肢は 3 つあります。最初に、このクラスをどのように構築するかについての意図を文書化して明確にすること、つまり、ユーザーがクラスのインスタンスを構築して使用できるようにすること、およびその方法を文書化することです。高レベルのクラスでこれらの Slide オブジェクトを追跡する必要があるため、モジュールのユーザーがこれらを自分と同じように作成できるように、コンストラクター インターフェイスが義務付けて設定するものを作成します。

もう 1 つの方法は、これらのインスタンスを作成および管理するクラスで公開したいこのクラスの側面をラップすることです。つまり、これを真の内部クラスにします。1 つのメソッド (たとえば、) のみを公開する必要がある場合、add_shape()これは非常に合理的です。ただし、このクラスへのインターフェースの大部分を公開したい場合、それはぎこちなく見え始めます。

最後に、ユーザーはこのクラスのインスタンスを自分で作成するべきではなく、インターフェイスの一部にアクセスできることを明確に文書化できます。これは紛らわしいですが、オプションの可能性があります。

私自身、最初の選択肢が好きです。クラスをモジュールのユーザーに公開し、そのコンストラクターとデストラクターの実装を使用して、それについて知る必要がある他のクラスに常に正しい方法で接続されるようにします。次に、ユーザーはこのオブジェクトをサブクラス化したり、呼び出したいメソッドをラップしたりして、オブジェクトが接続されたままになるようにします。

于 2013-08-31T22:31:55.010 に答える