14

プロジェクトにインポートする python-packages /MyLibPackage を作成します。

MyLibPackage.____init____.pymymodication.py が含まれています。さらに、MyLibPackage フォルダーには別のファイルが含まれています:base_classes.py(=外部プロジェクト)

mymodiciation.py は " from base_classes import *" をインポートします。

目標: base_classes (= 外部プロジェクト) のすべてのクラスを含む MyLibPackage をインポートできます。また、いくつかのクラスまたは関数を変更する必要がある場合は、これを mymodiciation.py に上書きできます。動作しますが、問題が発生しました。例えば:

mymodiciation.py でこのクラスを上書きします。

class Bookcollection(Bookcollection):
   new_member = "lalala"


class user(user):
   def get_books(self):
      return Bookcollection()

私が行った場合:

from MyLibPackage import *
x = user()
books = x.get_books()

この場合、オブジェクト Bookcollection にはプロパティ「new_member」があります。良い!しかし、私がこれを行う場合:

from MyLibPackage import *
x = shelf() #this class is not overwritten and used also the object "Bookcolelction"
books = x.get_books()

オブジェクト Bookcollection にはプロパティ「new_member」がありません

どのように言えばよいでしょうか: mymodiciation でクラスを上書きすると、呼び出しが MyLibPackage.base_classes.shelf (get_books) から来た場合でも、MyLibPackage はこれを使用する必要があります。

4

2 に答える 2

24

あなたがやりたいことは「モンキーパッチ」と呼ばれ、オブジェクト指向とはほとんど関係ありません。

Python はそれをサポートしていますが、すべてのクラスを制御できます。プロジェクトを真剣に見直して、本当に必要かどうかを確認する必要があります。

おそらく、Zope Component Architecture のようなフレームワークを使用することで、クラスをインターフェイスでマークすることができ、アダプター オブジェクトを提供することで、1 つのオブジェクトを最初に持つように設計されていないインターフェイスを持つものとしてきれいに使用できます。

そうは言っても、あなたが求めているのは、他のモジュールのクラスを変更して、他のすべてのモジュールに変更が見えるようにすることです。

あなたはまさにそれを行います: それが属するモジュールのクラスを変更します. Python では、元のモジュールで新しいクラスを目的の名前に割り当てるだけで実行できます。

import base_classes

class Bookcollection(base_classes.Bookcollection):
   new_member = "lalala"

base_classes.Bookcollection = Bookcollection

(このようなことが機能するためには、単一のスクリプトよりも大きなプロジェクトでは "from x import *" を避ける必要があります。この場合、コード全体で同じ名前で異なる意味を持つ 2 つの変数がありました: 基本クラスと、たとえば、継承されたクラス (Python 名前空間を使用すると、それを回避できます)。

したがって、これにより base_class モジュールの Bookcollection クラスが変更されますが、この時点から実行チェーンでそれを参照するコードに対してのみです。例の「x」クラスが「base_classes」モジュールで定義されている場合、または「MyModule」がインポートされる前に定義されている場合、古い「Bookcollection」クラスへの参照を取得します。

ご覧のとおり、すぐに混乱する可能性があります。実際にこのアプローチを選択した場合、プロジェクトを使用可能に保つ唯一の方法は、パッチを適用するすべてのクラスに実際にパッチが適用されていることを確認する単体テストを行うことです。ご覧のとおり、モジュールのインポート順序でも違いが生じます。テストの場所がある場合、サルのパッチを壊す順序でインポートを行うと、それらは壊れます。

既存のクラスに何かを追加して置き換えるだけの場合は、クラスを置き換えるためにモジュールにモンキー パッチを適用する代わりに、クラス自体にモンキー パッチを適用してそのコンポーネントを置き換えることができます。このように、モジュールのインポート順序はあまり重要ではありません。そのクラスの既存のインスタンスにも影響します:

 import base_classes

 base_classes.Bookcollection.new_member = "lalala"

 def new_bookcol_method(self):
      pass

 # to replace or register a method in the other class:
 base_classes.Bookcollection.__dict__["old_bookcol_method"] = new_bookcol_method

これにより、新しいクラス (それ自体がオブジェクト) を元のモジュールの同じ名前に割り当てようとするよりも一貫した動作が得られます。

全体として、@jamesjが彼の回答で示唆しているように、個別のクラスを使用するか、動的な動作が必要な場合は、Zope Component Architectureなどの保守可能なフレームワークを使用する必要があります。そして、どのようなアプローチを取るにしても、単体テストを作成してください。

于 2012-04-05T12:41:34.083 に答える
1

名前空間が衝突するため、同じ名前のクラスを使用することは一般的に悪い考えです。MyLibPackage.base_classes.Bookcollectionに変更することをお勧めしますMyLibPackage.base_classes.BaseBookcollection。これで、期待どおりに機能するはずです。

于 2012-04-05T11:54:33.297 に答える